9
\$\begingroup\$

See the diagram first:

When my AABB physics engine resolves an intersection, it does so by finding the axis where the penetration is smaller, then "push out" the entity on that axis.

Considering the "jumping moving left" example:

  • If velocityX is bigger than velocityY, AABB pushes the entity out on the Y axis, effectively stopping the jump (result: the player stops in mid-air).
  • If velocityX is smaller than velocitY (not shown in diagram), the program works as intended, because AABB pushes the entity out on the X axis.

How can I solve this problem?

Source code:

public void Update()
{
    Position += Velocity;
    Velocity += World.Gravity;

    List<SSSPBody> toCheck = World.SpatialHash.GetNearbyItems(this);

    for (int i = 0; i < toCheck.Count; i++)
    {
        SSSPBody body = toCheck[i];
        body.Test.Color = Color.White;

        if (body != this && body.Static)
        {                   
            float left = (body.CornerMin.X - CornerMax.X);
            float right = (body.CornerMax.X - CornerMin.X);
            float top = (body.CornerMin.Y - CornerMax.Y);
            float bottom = (body.CornerMax.Y - CornerMin.Y);

            if (SSSPUtils.AABBIsOverlapping(this, body))
            {
                body.Test.Color = Color.Yellow;

                Vector2 overlapVector = SSSPUtils.AABBGetOverlapVector(left, right, top, bottom);

                Position += overlapVector;
            }

            if (SSSPUtils.AABBIsCollidingTop(this, body))
            {                      
                if ((Position.X >= body.CornerMin.X && Position.X <= body.CornerMax.X) &&
                    (Position.Y + Height/2f == body.Position.Y - body.Height/2f))
                {
                    body.Test.Color = Color.Red;
                    Velocity = new Vector2(Velocity.X, 0);

                }
            }
        }               
    }
}

public static bool AABBIsOverlapping(SSSPBody mBody1, SSSPBody mBody2)
{
    if(mBody1.CornerMax.X <= mBody2.CornerMin.X || mBody1.CornerMin.X >= mBody2.CornerMax.X)
        return false;
    if (mBody1.CornerMax.Y <= mBody2.CornerMin.Y || mBody1.CornerMin.Y >= mBody2.CornerMax.Y)
        return false;

    return true;
}
public static bool AABBIsColliding(SSSPBody mBody1, SSSPBody mBody2)
{
    if (mBody1.CornerMax.X < mBody2.CornerMin.X || mBody1.CornerMin.X > mBody2.CornerMax.X)
        return false;
    if (mBody1.CornerMax.Y < mBody2.CornerMin.Y || mBody1.CornerMin.Y > mBody2.CornerMax.Y)
        return false;

    return true;
}
public static bool AABBIsCollidingTop(SSSPBody mBody1, SSSPBody mBody2)
{
    if (mBody1.CornerMax.X < mBody2.CornerMin.X || mBody1.CornerMin.X > mBody2.CornerMax.X)
        return false;
    if (mBody1.CornerMax.Y < mBody2.CornerMin.Y || mBody1.CornerMin.Y > mBody2.CornerMax.Y)
        return false;

    if(mBody1.CornerMax.Y == mBody2.CornerMin.Y)
        return true;

    return false;
}
public static Vector2 AABBGetOverlapVector(float mLeft, float mRight, float mTop, float mBottom)
{
    Vector2 result = new Vector2(0, 0);

    if ((mLeft > 0 || mRight < 0) || (mTop > 0 || mBottom < 0))
        return result;

    if (Math.Abs(mLeft) < mRight)
        result.X = mLeft;
    else
        result.X = mRight;

    if (Math.Abs(mTop) < mBottom)
        result.Y = mTop;
    else
        result.Y = mBottom;

    if (Math.Abs(result.X) < Math.Abs(result.Y))
        result.Y = 0;
    else
        result.X = 0;

    return result;
}
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

I just looked at the code I have not tried to prove where it is wrong.

I looked at the code and these 2 lines seemed strange:

if ((Position.X >= body.CornerMin.X && Position.X <= body.CornerMax.X) &&
(Position.Y + Height/2f == body.Position.Y - body.Height/2f))

You check for interval and then You check for euquality? I may be wrong, (there might be som rounding going on) but it seems it might cause a trouble.

\$\endgroup\$
0
\$\begingroup\$

It's hard to read code of other people, but I think this is one possible (purely brainstormed) workaround, although of course I'm not able to test it:

  1. Before the collision detection happens, save the players velocity to some temporary variable.
  2. After you've done your collision response, check if the players X or Y position has been corrected
  3. If X position has been changed, manually reset (as a kind of "safety reset") the players Y velocity to the one that he had before the response.

By the way, what happens with your current code when your players hits the roof while jumping?

\$\endgroup\$
4
  • \$\begingroup\$ Changing the velocity wouldn't solve anything, because the velocity is unaffected by the collision response. The response simply changes the Position of the player, leaving the Velocity unchanged. When the player hits the roof, it floats for a while then comes back down. It's intended since I'm not setting the Velocity to 0 when it hits the ceiling. \$\endgroup\$ Jun 20, 2011 at 21:12
  • \$\begingroup\$ What happens if you remove the code that sets one of either of the result vectors components to zero in the GetOverlapVector method? \$\endgroup\$
    – TravisG
    Jun 20, 2011 at 21:23
  • \$\begingroup\$ The entity gets pushed out diagonally, sometimes it doesn't even work correctly, other time it just snaps it like if it were on a grid. \$\endgroup\$ Jun 20, 2011 at 21:39
  • \$\begingroup\$ I can't make any sense of it right now. The way your entity is pushed out should depend on its distance from the body that it's colliding with, but your algorithm already does that. I'll take another look at it tomorrow. \$\endgroup\$
    – TravisG
    Jun 20, 2011 at 23:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .