Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (542)
Games in Android Showcase (133)
games submitted by our members
Games in WIP (606)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Collisions on y-axis in a side-scroller - debugging!  (Read 1518 times)
0 Members and 1 Guest are viewing this topic.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Posted 2011-04-25 09:55:55 »

EDIT: Please, if you've been here without being able to solve this, or don't want to because it's too long or my code is horrible to look at, please say so! Try to at least leave something I can use, even if you haven't found the philosophers stone Cool

Hey guys.

I'm writing a sidescroller with JumpingJack from KGPJ as reference.
I'm stuck with two types of collisions:
1. Jumping, and hitting your head against something.
2. Falling, and stopping when hitting ground below you. (this one works, but I think
the way I'm doing it is wrong - you'll see what I mean).

I'll just go over the structure of this, so you know enough to consider the problem.

Think of Mario. My world is made of bricks in the same way as Mario. You walk around on
these. The blocks are stored in an ArrayList[column] array.
In each of these ArrayLists are the blocks found in that columns, but in no order.

A block has it's own class, and instance  where it's x-World-coord (I'll explain this!),
and it's y-coord (that is just the y-coord on screen (the coordinate system used
when rendering stuff)), because the world will only scroll with the x-axis.

The world has it's own set of pixel-coordinates, but this is not important right now as
the world doesn't scroll so they are equal to the on-screen-coordinates.


Here's the problem:
When falling, the sprite has an increasing speed, so it might move with more than 1 pixel per update.
I need to do a check, if the sprite will land inside a block in the next planned move.
If it will, I need to calculate how long it WILL travel, so it will land on it's feet on top of the block.
The same basicly applies to jumping.

I got a working method for checking if a coordinate is inside a block.

Here's the solution provided in KGPJ for JumpingJack:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
public int checkBrickTop(int xWorld, int yWorld, int step)
    /* The sprite is moving downwards. It checks its next position
    (xWorld, yWorld) to see if it will enter a brick from above.

    If it does, then its step value is reduced to smallStep
    so it will only drop enough to touch the top of the brick.
     */

    {
        if (insideBrick(xWorld, yWorld)) {
            int yMapWorld = yWorld - (pHeight - height);
            int mapY = (int) (yMapWorld / Constants.brickHeight);  // map y- index
            int topOffset = yMapWorld - (mapY * Constants.brickHeight);
            int smallStep = step - topOffset;
            // System.out.println("top smallStep: " + smallStep);
            return smallStep;
        }
        return step;   // no change
    }  // end of checkBrickTop()


I changed it a little bit to match my own game, but the logic is the same.
This works as intended (falling, and the method above).

Now, when jumping and moving upwards I should be able to use almost the same method right?
This is the one rewritten from JumpingJack, to match my game just as the above method.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
public int checkBrickBase(int xWorld, int yWorld, int step)
    /* The sprite is moving upwards. It checks its next position
    (xWorld, yWorld) to see if it will enter a brick from below.

    If it does, then its step value is reduced to smallStep
    so it will only rise to touch the base of the brick.*/

    {
        if (insideBrick(xWorld, yWorld)) {
            int yMapWorld = yWorld - (pHeight - height);
            int mapY = (int) (yMapWorld / Constants.brickHeight);  // map y- index
            int topOffset = yMapWorld - (mapY * Constants.brickHeight);
            int smallStep = step - (Constants.brickHeight - topOffset);
            // System.out.println("base smallStep: " + smallStep);*/
            return smallStep;//smallStep;
        }
        return step;   // no change
    }  // end of checkBrickBase()


This one however does not work. Depending on how, and when I'm calling this I get different results
 - all wrong (not stopping the jump when hitting as it should).
This makes me think that either both these methods are wrong, or my updating of the jumping.

Here is how I use the methods:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
public void update(int delta) {
        if (isMovingRight) {
            xWorld += moveSize;
            locX += moveSize;
        } else if (isMovingLeft) {
            xWorld -= moveSize;
            locX -= moveSize;
        }
        if (!isRising) {
            if (!bricksMan.insideBrick(xWorld + (getWidth() / 2),
                    yWorld + getHeight()))
                /* If this happens then there is not ground under Karl */ {
                // increase falling speed
                yVelocity += 0.5;
                double fallDistance = bricksMan.checkBrickTop(
                        (int) xWorld + (getWidth() / 2),
                        (int) yWorld + (getHeight() + (int) yVelocity), (int) yVelocity);
                yWorld += fallDistance;
                locY += fallDistance;
            } else {
                /* ground has been hit */
                yVelocity = 0;
            }
        } else if (isRising)// is rising
        {
            if (yVelocity > -10) // is moving towards air
            {
                if (!bricksMan.insideBrick(xWorld + (getWidth() / 2), yWorld)) {
                    /* There is air above Karl */
                    yVelocity -= -0.5;
                    double riseDistance = bricksMan.checkBrickBase(
                            (int) xWorld + (getWidth() / 2),
                            (int) yWorld + (int) yVelocity, (int) yVelocity);
                    System.out.println("riseDistance and yVelocity: " +riseDistance+" "+yVelocity);
                    yWorld += riseDistance;
                    locY += riseDistance;
                } else {
                    yVelocity = 0; // stop getting any higher!!
                    isRising = false;
                }
            }
            if (yVelocity >= 0.0) // nothing was hit, but we ran out of velocity
            {
                isRising = false;
                yVelocity = 0;
            }

        }
    }

NOTES:
1) locY and the yWorld-fields are the same right now, since the world is not scrolling!
2) Karl is the player.
3) yVelocity is a double.
4) collisions on the x-axis is dealt with before calling update().
5) when jumping yVelocity is set to -9.5, and isRising it set to true.

I tried to implement the falling, and jumping part of the collision detection in the same way,
but it appears I haven't as only the falling works right.
Can you spot the problem with the code?

I'll happily supply any more info about the logic if anything it unclear to you Smiley


TL;DR VERSION:
Please read the text Smiley



Offline aazimon
« Reply #1 - Posted 2011-04-26 19:52:16 »

Why are you checking the yVelocity when moving up and not down? I don't think you need to do that. Just check if Karl hit his head.
  The line yVelocity -= -0.5; looks confusing because you have a double negative. yVelocity += 0.5; // slow down upward speed.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #2 - Posted 2011-04-27 18:56:07 »

Why are you checking the yVelocity when moving up and not down? I don't think you need to do that. Just check if Karl hit his head.
  The line yVelocity -= -0.5; looks confusing because you have a double negative. yVelocity += 0.5; // slow down upward speed.

That's because his next position on the y-axis is currentY + yVelocity. yVelocity is the number of pixels he will move on the y-axis.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline aazimon
« Reply #3 - Posted 2011-04-27 20:40:55 »

So, when he jumps does he "go through" blocks above him? Are you checking his current location or his new location (currentY + yVelocity) for the collision? You should be checking the new location.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #4 - Posted 2011-04-28 18:05:09 »

So, when he jumps does he "go through" blocks above him? Are you checking his current location or his new location (currentY + yVelocity) for the collision? You should be checking the new location.

The location after the move.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
} else if (isRising)// is rising
        {
            if (yVelocity > -10) // is moving towards air
            {
                if (!bricksMan.insideBrick(xWorld + (getWidth() / 2), yWorld)) {
                    /* There is air above Karl */
                    yVelocity -= -0.5;
                    double riseDistance = bricksMan.checkBrickBase(
                            (int) xWorld + (getWidth() / 2),
                            (int) yWorld + (int) yVelocity, (int) yVelocity);
                    System.out.println("riseDistance and yVelocity: " +riseDistance+" "+yVelocity);
                    yWorld += riseDistance;
                    locY += riseDistance;
                } else {
                    yVelocity = 0; // stop getting any higher!!
                    isRising = false;
                }
            }
            if (yVelocity >= 0.0) // nothing was hit, but we ran out of velocity
            {
                isRising = false;
                yVelocity = 0;
            }


See the double riseDistance Smiley

Offline aazimon
« Reply #5 - Posted 2011-04-28 20:17:37 »

Let me see if I follow your code right:
In checkBrickBase(). If you pass in someX, 95, -9. Ignoring x coordinates.
The location is in brick at Y 90 to 100. 10 Brick height, you want to return -4 or smallStep =  yWorld + step - topOffset.
Then your Y in the update method would start out as 104 and would end up at 100.
Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #6 - Posted 2011-04-29 06:31:02 »

Let me see if I follow your code right:
In checkBrickBase(). If you pass in someX, 95, -9. Ignoring x coordinates.
The location is in brick at Y 90 to 100. 10 Brick height, you want to return -4 or smallStep =  yWorld + step - topOffset.
Then your Y in the update method would start out as 104 and would end up at 100.


It's supposed to take the new position as well as the amount risen as parameters. Then it'll return the amount Karl SHOULD rise so he doesn't hit a brick with his head. So, if the next move will be inside a brick it returns small-step so KArl only moves up to the brick and no further.

Offline dishmoth
« Reply #7 - Posted 2011-04-29 10:51:38 »

It looks to me like the step you're passing into checkBrickBase() is negative, but the function is expecting the value to be positive (and is trying to return a positive value in response).

Try this instead:
1  
2  
3  
double riseDistance = -bricksMan.checkBrickBase(
                            (int) xWorld + (getWidth() / 2),
                            (int) yWorld + (int) yVelocity, (int) -yVelocity);

(Note the two extra minus signs.)

Other comments:
  • It's possible you could run into problems mixing integers and floating-point numbers in this sort of game.  I'd suggest keeping everything as integers as far as possible (and fixing the frame rate of the game loop).  Otherwise you have to be very careful when casting doubles to ints.  For example, (int)yWorld+(int)yVelocity will not always be the same as (int)(yWorld+yVelocity).  In the worst case, these issues could lead to you character sometimes getting stuck on the edges of blocks.
  • Don't forget to add a maximum speed when falling.  You don't want your character to fall so fast that he/she skips a block.
  • Personally I'd get rid of the isRising variable, and use the sign of yVelocity to determine whether the character is going up or down.  But I would have an onGround variable to indicate when the character is standing on a block.  (You can't tell from yVelocity alone whether the character is airborne -- a zero value means either standing on the ground or at the top of a jump.)  But that's just personal taste.
  • Yes, you're original post was far too long. Tongue  I wouldn't have bothered reading it if there was something better on the telly (it's all some stupid wedding thing).

Simon

Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #8 - Posted 2011-04-29 14:33:50 »

 Pointing Pointing Pointing Pointing
It looks to me like the step you're passing into checkBrickBase() is negative, but the function is expecting the value to be positive (and is trying to return a positive value in response).

Try this instead:
1  
2  
3  
double riseDistance = -bricksMan.checkBrickBase(
                            (int) xWorld + (getWidth() / 2),
                            (int) yWorld + (int) yVelocity, (int) -yVelocity);

(Note the two extra minus signs.)

Other comments:
  • It's possible you could run into problems mixing integers and floating-point numbers in this sort of game.  I'd suggest keeping everything as integers as far as possible (and fixing the frame rate of the game loop).  Otherwise you have to be very careful when casting doubles to ints.  For example, (int)yWorld+(int)yVelocity will not always be the same as (int)(yWorld+yVelocity).  In the worst case, these issues could lead to you character sometimes getting stuck on the edges of blocks.
  • Don't forget to add a maximum speed when falling.  You don't want your character to fall so fast that he/she skips a block.
  • Personally I'd get rid of the isRising variable, and use the sign of yVelocity to determine whether the character is going up or down.  But I would have an onGround variable to indicate when the character is standing on a block.  (You can't tell from yVelocity alone whether the character is airborne -- a zero value means either standing on the ground or at the top of a jump.)  But that's just personal taste.
  • Yes, you're original post was far too long. Tongue  I wouldn't have bothered reading it if there was something better on the telly (it's all some stupid wedding thing).

That worked like a charm! :O One sign..

I wanted to give you the feeling of knowing whats going on here, because the problem could've been a hundred places Tongue
If I only used integers, how do I add velocity? Right now I just increase or decrease by 0.5 because that value is a double.. How do I do that with integers? I don't wan't my dear Karl to increase the speed double as fast as now  Cheesy

Thank you a lot for reading it all and coming up with nice post

Offline dishmoth
« Reply #9 - Posted 2011-04-29 15:27:28 »

If I only used integers, how do I add velocity? Right now I just increase or decrease by 0.5 because that value is a double.. How do I do that with integers? I don't wan't my dear Karl to increase the speed double as fast as now  Cheesy

It's a tricky question.  I don't know how other people would handle it, but I'd suggest one of two approaches.

1. Don't worry about it too much. Grin  Carry on using doubles, but keep in mind that you've got to be careful when casting to integers.  For example, the following code
1  
2  
if ( !insideBrick((int)x, (int)y+(int)yVelocity) )  y += yVelocity;
assert( !insideBrick((int)x, (int)y) );

could fail the assertion (Karl inadvertently jumps inside a brick) because of the mixture of ints and doubles.  And remember that you can run into similar problems because of numerical rounding issues with floating-point numbers.

2. Instead of working in units of pixels, work in tenths of a pixel.  (Or hundredths, or (1/256)-ths, or whatever.)  Then you would be changing the velocity by 5 each step, rather than 0.5.  No more doubles!  This is either a very practical solution, or an ugly hack, depending on your tastes. Tongue

Simon

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline aazimon
« Reply #10 - Posted 2011-04-29 22:34:42 »

Let me see if I follow your code right:
In checkBrickBase(). If you pass in someX, 95, -9. Ignoring x coordinates.
The location is in brick at Y 90 to 100. 10 Brick height, you want to return -4 or smallStep =  yWorld + step - topOffset.
Then your Y in the update method would start out as 104 and would end up at 100.


It's supposed to take the new position as well as the amount risen as parameters. Then it'll return the amount Karl SHOULD rise so he doesn't hit a brick with his head. So, if the next move will be inside a brick it returns small-step so KArl only moves up to the brick and no further.

So you would be passing in like 105 and would expect to get -4 back so he is at 101. You want to check if 105 - 9 is inside a box. Change if (insideBrick(xWorld, yWorld)) -> if (insideBrick(xWorld, YWorld + step). Then use the same smallStep =  yWorld + step - topOffset.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

Elsealabs (20 views)
2014-12-28 10:39:27

CopyableCougar4 (21 views)
2014-12-28 02:10:29

BurntPizza (25 views)
2014-12-27 22:38:51

Mr.CodeIt (15 views)
2014-12-27 04:03:04

TheDudeFromCI (20 views)
2014-12-27 02:14:49

Mr.CodeIt (26 views)
2014-12-23 03:34:11

rwatson462 (60 views)
2014-12-15 09:26:44

Mr.CodeIt (48 views)
2014-12-14 19:50:38

BurntPizza (98 views)
2014-12-09 22:41:13

BurntPizza (116 views)
2014-12-08 04:46:31
How do I start Java Game Development?
by gouessej
2014-12-27 19:41:21

Resources for WIP games
by kpars
2014-12-18 10:26:14

Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!