1
\$\begingroup\$

I've implemented a collision library for a character who is an AABB and am resolving collisions between AABB vs AABB and AABB vs OBB. I wanted slopes for certain sections, so I've toyed around with using several OBBs to make one, and it's working great except for one glaring issue; The collision resolution on the corner of an OBB makes the player's AABB jitter up and down constantly. I've tried a few things I've thought of, but I just can't wrap my head around what's going on exactly. Here's a video of what's happening as well as my code:

Here's the function to get the collision resolution (I'm likely not doing this the right way, so this may be where the issue lies):

public Vector2 GetCollisionResolveAmount(RectangleCollisionObject resolvedObject, OrientedRectangleCollisionObject b)
{
    Vector2 overlap = Vector2.Zero;
    LineSegment edge = GetOrientedRectangleEdge(b, 0);


    if (!SeparatingAxisForRectangle(edge, resolvedObject))
    {
        LineSegment rEdgeA = new LineSegment(), rEdgeB = new LineSegment();
        Range axisRange = new Range(), rEdgeARange = new Range(), rEdgeBRange = new Range(), rProjection = new Range();
        Vector2 n = edge.PointA - edge.PointB;

        rEdgeA.PointA = RectangleCorner(resolvedObject, 0);
        rEdgeA.PointB = RectangleCorner(resolvedObject, 1);
        rEdgeB.PointA = RectangleCorner(resolvedObject, 2);
        rEdgeB.PointB = RectangleCorner(resolvedObject, 3);
        rEdgeARange = ProjectLineSegment(rEdgeA, n);
        rEdgeBRange = ProjectLineSegment(rEdgeB, n);
        rProjection = GetRangeHull(rEdgeARange, rEdgeBRange);
        axisRange = ProjectLineSegment(edge, n);

        float axisMid = (axisRange.Maximum + axisRange.Minimum) / 2;
        float projectionMid = (rProjection.Maximum + rProjection.Minimum) / 2;

        if (projectionMid > axisMid)
        {
            overlap.X = axisRange.Maximum - rProjection.Minimum;
        }
        else
        {
            overlap.X = rProjection.Maximum - axisRange.Minimum;
            overlap.X = -overlap.X;
        }
    }

    edge = GetOrientedRectangleEdge(b, 1);
    if (!SeparatingAxisForRectangle(edge, resolvedObject))
    {
        LineSegment rEdgeA = new LineSegment(), rEdgeB = new LineSegment();
        Range axisRange = new Range(), rEdgeARange = new Range(), rEdgeBRange = new Range(), rProjection = new Range();
        Vector2 n = edge.PointA - edge.PointB;

        rEdgeA.PointA = RectangleCorner(resolvedObject, 0);
        rEdgeA.PointB = RectangleCorner(resolvedObject, 1);
        rEdgeB.PointA = RectangleCorner(resolvedObject, 2);
        rEdgeB.PointB = RectangleCorner(resolvedObject, 3);
        rEdgeARange = ProjectLineSegment(rEdgeA, n);
        rEdgeBRange = ProjectLineSegment(rEdgeB, n);
        rProjection = GetRangeHull(rEdgeARange, rEdgeBRange);
        axisRange = ProjectLineSegment(edge, n);

        float axisMid = (axisRange.Maximum + axisRange.Minimum) / 2;
        float projectionMid = (rProjection.Maximum + rProjection.Minimum) / 2;

        if (projectionMid > axisMid)
        {
            overlap.Y = axisRange.Maximum - rProjection.Minimum;
            overlap.Y = -overlap.Y;
        }
        else
        {
            overlap.Y = rProjection.Maximum - axisRange.Minimum;
        }
    }

    return overlap;            
}

And here is what I'm doing to resolve it right now:

if (collisionDetection.OrientedRectangleAndRectangleCollide(obb, player.PlayerCollision))
{
    var resolveAmount = collisionDetection.GetCollisionResolveAmount(player.PlayerCollision, obb);

    if (Math.Abs(resolveAmount.Y) < Math.Abs(resolveAmount.X))
    {
        var roundedAmount = (float)Math.Floor(resolveAmount.Y);
        player.PlayerCollision._position.Y -= roundedAmount;
    }
    else if (Math.Abs(resolveAmount.Y) <= 30.0f) //Catch cases where the player should be able to step over the top of something
    {
        var roundedAmount = (float)Math.Floor(resolveAmount.Y);
        player.PlayerCollision._position.Y -= roundedAmount;
    }
    else
    {
        var roundedAmount = (float)Math.Floor(resolveAmount.X);
        player.PlayerCollision._position.X -= roundedAmount;
    }
}

Can anyone see what might be the issue here, or has anyone experienced this before that knows a possible solution? I've tried for a few days to figure this out on my own, but I'm just stumped.

Edit: I've added another video that more clearly demonstrates what's going on.

\$\endgroup\$
  • \$\begingroup\$ I took a quick look but didn't find anything wrong, have you tried printing the ID of the box you are colliding with? By watching the video I could see that the jitter is caused when the character is standing between two boxes, I'm pretty sure the character is getting multiple collision responses in the same spot because is detecting 2 collisions. Have you tried using an sliding ellipse for the character? It's an easier solution and gives the best responses on collisions \$\endgroup\$ – nosmirck Nov 6 '13 at 12:09
  • \$\begingroup\$ Yeah, I had thought about that, but it happens even when there is just a single box to collide with. I've added a video to the original post that more clearly demonstrates it. As for using a sliding ellipse, I'm only supporting AABB and OBB collision resolution with this home-brew collision. I've thought about just adding ellipse support for making curved surfaces, but I'm not sure where to start to implement it. \$\endgroup\$ – patt4179 Nov 6 '13 at 14:10
2
\$\begingroup\$

I ended up implementing an AABB/Line Segment collision just to test if it would also have these issues, and i ended up figuring out the problem. It was an issue with my collision resolution.

When the AABB was resting on the corner of an OBB or on the top-most point of a line segment, it was resolving the Y resolution amount using the formula for the line, but was plugging in an X value that was outside of its range.

Here's the line segment/AABB resolution code I ended up going with:

public Vector2 GetCollisionResolveAmount(RectangleCollisionObject resolvedObject, LineSegmentCollisionObject line)
{
    Vector2 overlap = Vector2.Zero;
    var topRight = new Vector2(resolvedObject.Position.X + resolvedObject.HalfWidth, resolvedObject.Position.Y - resolvedObject.HalfHeight);
    var topLeft = new Vector2(resolvedObject.Position.X - resolvedObject.HalfWidth, resolvedObject.Position.Y - resolvedObject.HalfHeight);
    var bottomLeft = new Vector2(resolvedObject.Position.X - resolvedObject.HalfWidth, resolvedObject.Position.Y + resolvedObject.HalfHeight);
    var bottomRight = new Vector2(resolvedObject.Position.X + resolvedObject.HalfWidth, resolvedObject.Position.Y + resolvedObject.HalfHeight);

    var leftLinePoint = line.PointA.X < line.PointB.X ? line.PointA : line.PointB;
    var rightLinePoint = line.PointA.X > line.PointB.X ? line.PointA : line.PointB;

    if (leftLinePoint.Y > rightLinePoint.Y) //Line is rotated to the left
    {
        float diffOne = Math.Abs(FindYGivenX(leftLinePoint, rightLinePoint, bottomRight.X) - bottomRight.Y);
        float diffTwo = Math.Abs(FindYGivenX(leftLinePoint, rightLinePoint, topLeft.X) - topLeft.Y);

        if (diffOne < diffTwo) //We're above the line
        {
            if(bottomRight.X > rightLinePoint.X)
                diffOne = Math.Abs(rightLinePoint.Y - bottomRight.Y);
            overlap.Y = diffOne * -1;
        }
        else //We're below the line
        {
            overlap.Y = diffTwo;
        }
    }
    else if (rightLinePoint.Y > leftLinePoint.Y) //Line is rotated to the right
    {
        float diffOne = Math.Abs(FindYGivenX(leftLinePoint, rightLinePoint, bottomLeft.X) - bottomLeft.Y);
        float diffTwo = Math.Abs(FindYGivenX(leftLinePoint, rightLinePoint, topRight.X) - topRight.Y);

        if (diffOne < diffTwo) //We're above the line
        {
            if (bottomLeft.X < leftLinePoint.X)
                diffOne = Math.Abs(leftLinePoint.Y - bottomLeft.Y);

            overlap.Y = diffOne * -1;
        }
        else //We're below the line
        {
            overlap.Y = diffTwo;
        }
    }
    else if (rightLinePoint.X == leftLinePoint.X) //Line is vertically straight
    {
        if (resolvedObject.Position.X < line.PointA.X)
        {
            overlap.X = Math.Abs(topRight.X - line.PointA.X) * -1;
        }
        else
        {
            overlap.X = Math.Abs(line.PointA.X - topLeft.X);
        }
    }
    else //Line is horizontally straight
    {
        if (resolvedObject.Position.Y < line.PointA.Y) //above
        {
            overlap.Y = Math.Abs(bottomLeft.Y - line.PointA.Y) * -1;
        }
        else
        {
            overlap.Y = Math.Abs(topLeft.Y - line.PointA.Y);
        }
    }

    return overlap;
}
|improve this answer|||||
\$\endgroup\$

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.