3
\$\begingroup\$

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:

Collision detection function

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.

Diagram showing the bug

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.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ You may be interested in Resolving collisions after initial collision or Nestling into contact with a group of physics objects. \$\endgroup\$
    – DMGregory
    May 4, 2022 at 11:54
  • 1
    \$\begingroup\$ Thanks for the detailed explanation and visualization, and also thanks for providing the pseudo code. It really helped me to understand the concept. I think I solved the glitching problem by resolving for multiple collisions in a single pass. When inside a narrowing corridor, next position usually collides with 2 walls; so subtracting the normal vectors for both walls seems to done the trick for me. \$\endgroup\$
    – Egemenk
    Dec 30, 2022 at 8:38

0

You must log in to answer this question.