Platform jumping problems with AABB collisions
Asked Answered
S

3

6

Diagram

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;
    }
Statolatry answered 20/6, 2011 at 19:58 Comment(1)
You should consider posting this on www.gamedev.stackexchange.com. That said, I'm digging into the code right now to see if I can help you.Gamez
S
0

One possible solution I found is sorting the objects before resolving based on the velocity of the player.

Statolatry answered 22/9, 2014 at 10:24 Comment(0)
G
1

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?

Gamez answered 20/6, 2011 at 21:3 Comment(1)
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.Statolatry
H
0

I had that same problem. Even the Microsoft platformer starterskit seems to have that bug.

The solution I found so far is to either use multisampling (argh) or to use the movedirection of the object: only move the distance between those 2 objects and no further when a collision is detected (and do that for each axis).

Harbour answered 7/8, 2011 at 17:48 Comment(0)
S
0

One possible solution I found is sorting the objects before resolving based on the velocity of the player.

Statolatry answered 22/9, 2014 at 10:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.