TLDR
My collision resolution algorithm with wall sliding works perfect for multiple walls but it fails when the angle of the corner of two walls is less than 90 degrees. (acute angles) and the player end up glitching though the walls.
My problem
I implemented collision detection in my 3d first person game and it works fine. My problem is with the collision resolution. The collisions are fully in 2D since the game is 100% on the ground and doesn't involve jumping.
The game world is composed of players represented as circles and obstacles represented as AABB, or axis aligned triangles.
I've fully implemented collision detection against all shapes and it works perfect. I implemented a single entry point method that checks for collision against all other objects near the player and returns the nearest contact point.
public bool TestTerrainCollision(Vector2 playerPosition, float playerRadius, out Vector2 contactPoint)
{
// Loops through all walls near the player and checks the intersection
// Returns if there was any intersection (true/false)
// and the contact point of the NEAREST intersection.
}
This is an example diagram of the output of this method:
The problem relies with the collision resolution. This is the pseudocode of the algorithm I'm using. It looks long because I added a lot of comments, but is pretty staightforward:
// Player update
public void Update()
{
// Process input and calculates a new 2d velocity vector.
Vector2 velocity = GetInputVelocity();
// Calculate new player's position
Vector2 newPosition = this.Position + velocity;
while (velocity.LengthSquared() > 0.001f)
{
// Call my world collision detection function
if (! world.TestTerrainCollision(newPosition, this.Radius, out Vector2 contactPoint))
{
break; // If there is not collision, break
}
// Calculate the normal vector using the contact point
Vector2 normal = Vector2.Normalize(newPosition - contactPoint);
// Calculate the penetration distance, this is the distance the circle
// has penetrated though the wall.
float distToContactPoint = Vector2.Distance(newPosition, contactPoint);
float penetrationDist = this.Radius - distToContactPoint;
// Adjust the position to the position of impact plus a small offset
newPosition += normal * (penetrationDist + 0.001f);
// "Slide" Against Walls
// This projects the remaining velocity onto the tangent of the hit surface
// More info in this video: https://www.youtube.com/watch?v=oom6R-M2lvQ
velocity -= normal * Vector2.Dot(velocity, normal);
// Now the while loop will continue to test for a collision with the new position and remaining velocity
}
// Update the position
this.Position = newPosition;
}
The algorithm works perfect against axis aligned walls and diagonal walls and even works fine when the angle between the walls is >= 90 deg
but it fails when when the angle is < 90 deg
.
The problem is when the angle is < 90 deg
because first the nearest wall collision is solved (Wall A), and the the player "slides" against the normal of that wall and ends up inside the other wall (Wall B). Then in the next iteration, the collision with the Wall B is solved, the player slides and ends up again inside the Wall A. The problem is that in that process the player advanced a little towards the corner, so if this process is repeated in a lot of frames, the player can glitch though the walls. Also the collisions look "bumpy".
I would like any tip or advice or technique to solve this problem. Thank you.