3
\$\begingroup\$

I'm facing a problem when I try to resolve the collision between two units using floats as positions of the units in my game.

I am detecting the collisions using the SweptAABB algorithm and if there is a collision I fix it by using the normalized collision time that the SweptAABB function returns. Something like this:

collisionTime = SweptAABB(unitA, platform)
fixedPosition = (currentPos - previousPos) * collisionTime;

This generally works fine but not always, due of the floats precision errors the fixedPosition can result in a VERY SMALL overlap. For example: suppose that the fixedPosition for the Y axis should be 12.345234 to avoid the collision but instead of getting that value (after multiplying by the collisonTime) I get something like 12.345239 (a difference of 0.000005) and when eventually the overlap method is executed it will still detecting the overlap:

public boolean overlap(AABB b) {
    Vector2 distance = new Vector2(b.position).substract(position);

    return Math.abs(distance.x) < (extents.x + b.extents.x)
        && Math.abs(distance.y) < (extents.y + b.extents.y);
}

So I decided to start changing the code so that I can get the face of the box that collides and use simple arithmetic addition of the positions instead of multiplication but this also fails for the same reason. For instance, suppose this:

// unitA collides with unitB at this point in the X axis:
float unitB_Face_X = 0.7466669f;
// since position is defined at the center of the unit I move unitA to the right:
float unitA_Fix_X = unitB_Face_X + 2;   

But unitA_Fix_X will result in a slightly different value (2.746667) that will eventually be detected as an overlap.

Just a clarification: My game is like Super Mario Bros, a 2D platform. I dont need fine precision, two decimals places (or one) is more than enough for my game world (each unit character has 1f for width and 2f for height).

I think that there must be a correct way to deal with this kind of problem (every 2d platform game out there has solved this problem). I have only think in one solution: Use integers instead of floats for the position of every unit in my game. I cannot avoid floats completly because of deltaTime (also libgdx uses them for the sprites positions and rendering), but I think I can make the integer conversion at the begining of each frame by doing something like this:

float fPos = unitA.velocity * deltaTime;
int intPosition = (int)(fPos * 1000);

And use the intPosition for detecting and resolving the collisions. After that I will have to convert it back to float for rendering the sprites.

Is this a bad approach? I dont want to shoot my self in the foot. Thanks in advance.

\$\endgroup\$
1
\$\begingroup\$

Common practice dealing with imprecision of floats is to introduce Epsilon to all "near-zero" checks e.g.:

const float Epsilon = 0.00001;
public boolean overlap(AABB b) {
    Vector2 distance = new Vector2(b.position).substract(position);

    return Math.abs((Math.abs(distance.x) - (extents.x + b.extents.x))) > Epsilon 
        && Math.abs((Math.abs(distance.y) - (extents.y + b.extents.y))) > Epsilon ;
}

Fixed precision(as you suggested) could be solution too, but it basically requires you to implement whole new numeric type - and as always lot of extra code(untested by year of usage) can, and probably will, lead to bugs and errors. If you find it more comfortable and you are willing to risk spending several extra hours in debugger, there is no reason not-to go for it.

\$\endgroup\$
  • \$\begingroup\$ Sorry for the really delayed response but I have been really busy, but recently I been able to implement your suggestion and for now is working flawless, so thanks! \$\endgroup\$ – mevqz Jan 3 '16 at 20:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.