Collision Detection Problem

Started by
14 comments, last by Narf the Mouse 13 years, 11 months ago
Hello everybody! I'm kind of going crazy over an absurd problem of collision detection. Basically, the system ( which is HGE ) reports a collision when my player box is standing on the floor ( player_box.low_y = floor.high_y ). This not only does not make any sense, but the same result does not happen when I hardcode two boxes one standing over the other, so I don't really know what to think.. This is my code:
void Fighter::think () {
    // H COLLISIONS HANDLING
    hgeRect fTry = fSpace;				// Copies Fighter bounding box
    // hSpeed > 0 :     RIGHT
    // hSpeed = 0 :     NOT MOVING
    // hSpeed < 0 :     LEFT
    float hSpeed = fSoul.getDx();		// Gets Fighter horizontal speed

    if ( hSpeed != 0 ) {				// If there is a possible collision to manage..

        // Projects Self into the Future
        if ( hSpeed > 0 )				// Expands the bounding box in the moving direction
            fTry.x2 += hSpeed;
        else
            fTry.x1 += hSpeed;

        // if cannot go there ( foreach square TODO )
        if ( fTry.Intersect( &rightWall ) ) {
            // Limits hSpeed
            if ( hSpeed > 0 )
                fSoul.setDx( rightWall.x1 - fSpace.x2 );	// Limits speed to space between Fighter and the wall
            else
                fSoul.setDx( rightWall.x2 - fSpace.x1 );
        }

    }

    // V COLLISIONS HANDLING
    fTry = fSpace;						// Resets try bounding box
    // vSpeed > 0 :     FALLING
    // vSpeed = 0 :     NOT MOVING
    // vSpeed < 0 :     RAISING
    float vSpeed = fSoul.getDy();		// Gets Fighter vertical speed

    if ( vSpeed != 0 ) {				// If there is a possible collision to manage..
										// NOTE: Due to gravity, vSpeed is usually never 0; at least 0.3
        // Projects Self into the Future
        if ( vSpeed > 0 )
            fTry.y2 += vSpeed;
        else
            fTry.y1 += vSpeed;

        // Cannot go there ( foreach square TODO )
        if ( fTry.Intersect( &floor ) ) {
            // FLOW ENTERS HERE EVEN THOUGH IT SHOULDN'T
            // Limits hSpeed
            if ( vSpeed > 0 ) {
                fSoul.setDy( floor.y1 - fSpace.y2 );
                // Resets Available Jumps
                fJumps = N_JUMPS;
            }
            else ; // AND ENDS UP HERE
                // commented this because otherwise my sprite vanishes =P
                // fSoul.setDy( floor.y2 - fSpace.y1 );
        }
    }
}

The handling for the horizontal and vertical part are exactly the same, but the problem rises when I try to jump after I'm landed on the floor. Then the game detects an intersection between my sprite and the floor, even though that is impossible ( I've debugged tracking all the variables.. ). It seems all right to me =( If you need additional information just let me know! Thanks in advance for any advices!
Advertisement
One thing that comes to mind is floating point precision errors. If you have two floating point numbers (I assume that player_box.low_y and floor.high_y is floating point and not integers), you can't guarantee that the check (player_box.low_y == floor.high_y) returns true even if they seems to be equal. In the same way, you can't guarantee that player_box.low_y - floor.high_y returns 0, even if they are equal.

I had an error like that yesterday as well, and it took a while to find the problem (collisions were missed randomly, or so it seemed). I noticed that when my collision box' lower y value and the ground surface's y value were equal, the distance wasn't 0, it was something like 1e-15, or sometimes -1e-15 (or some other very small number).

So what you can try is what I did, do a "if (abs(player_box.low_y - floor.high_y) < 1e-12) distance = 0; else distance = player_box.low_y - floor.high_y;" on your distance calculations, and other floating point operations which may be sensitive to precision errors.

Hope this helps.

Edit: My numbers are based on double precision floats. Since you're using single precision floats, you might need a higher threshold.
Also, if your sprite is subject to gravity, it might go like this: Sprite lands. Sprite is dragged down Y distance by gravity. Sprite is checked for a collision, which now exists. Sprite is moved up Y distance to resolve the collision.

From the computer's viewpoint, the sprite is always colliding. But, because the collision is fixed before the player can see it...

Edit: Nevermind, you check for that. Provided that check works, it shouldn't happen.
Thanks for the heads up!

I've checked my code and these are the results:

- if I put a
if ( player_box.low_y != floor.high_y )    check_collision_code(); 
it works. ( but I cannot use this solution as it introduces a new couple of bugs, and it's also bad code which I really cannot stand )

- if I put a
if ( abs( player_box.low_y - floor.high_y ) < 1e-12 )     player_box.low_y = floor.high_y; 
before the check collision code it still doesn't work, even though it actually enters the if.

There are another couple of things I didn't say about this problem:

1) I've debugged my code, and usually the variable watches signal when there is a problem with floats ( for example, my gravity offset is reported as 0.29999999998 ), but the Ys of the two boxes do not have any floating part.

2) If I set the floor bounding box a little more higher, let's say above 600, my code works. If I move the floor lower, at 700, the bug reappears. This could be related to non-integer movement; though to me it seems unlikely, because the limit that trigger the bug is precise ( 639 ).


Also, is there a way to automatically round floats to max 2 decimal digits? If I could do that I could avoid in block all this kind of problems..
My first instinct there would be to check collision to make sure I'm checking against either all relative or all absolute positions and not, say, relative and absolute positions, somewhere.
I'm sure I'm doing the check with the absolute position of every box, mostly because I never use a relative position for anything =P
...I got nothing. Try stepping through your code, if you can?
You mean debugging step by step?

If so.. already done =(
Could you post your intersect function and tell what x1,x2 and y1,y2 stand for?
x1, y1 stand for the coordinates of the top-left vertex of the box.
x2, y2 stand for the coordinates of the bottom-right vertex of the box.

The intersection function is not mine, it's the one written in the HGE engine, so I think it's correct.

I've found the bug though.
It's here, when I expand the fighter's box:

if ( vSpeed > 0 )     fTry.y2 += vSpeed;else     fTry.y1 += vSpeed; // This triggers the bug with the intersection


Now I've found it.. but still this line shouldn't trigger a collision downwards!

Ideas?

This topic is closed to new replies.

Advertisement