Advertisement

AABB vs OBB Collision Resolution Jitter on Corners

Started by November 05, 2013 05:14 AM
1 comment, last by erpatton 11 years, 3 months ago

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.

I've added another video that more clearly shows what's going on. I still haven't figured out what's happening with this. Would it be easier to just implement ellipses or curves for my sloped surfaces? If so, does anyone know a good starting point for adding collision detection/resolution for ellipses to my existing AABB/OBB implementation?

Advertisement
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;
}

This topic is closed to new replies.

Advertisement