2
\$\begingroup\$

I have a working SAT implementation, but I am running into a problem where direct collisions at a corner do not work for tiled surfaces. That is, it clips on the surface when going in a certain direction because it gets hung up on one of the tiles, and so, for example, if I walk across a floor while holding both down and left, the player will stop when meeting the next shape because the player will be colliding with the right side rather than with the top of the floor tile.

This illustration shows what I mean:

SAT errors

The top block will translate right first and then up.

I have checked here and here which are helpful, but this does not address what I should do in a situation where I don't have a tile-based world. My usage of the term "tile" before isn't really accurate since what I'm doing here is manually placing square obstacles next to each other, not assigning them spots on a grid.

What can I do to fix this?

\$\endgroup\$
4
  • \$\begingroup\$ I was working on a 2D collision system and ran into the exact same problem... good thing I looked at the questions before posting my own! \$\endgroup\$
    – DMan
    Commented Nov 18, 2012 at 4:23
  • \$\begingroup\$ @DMan If you know of a duplicate question than could you please let me know where it is instead of assuming that I didn't look? \$\endgroup\$
    – ssb
    Commented Nov 18, 2012 at 5:56
  • \$\begingroup\$ Oh no, it wasn't meant as a jab towards you (I see where you misunderstood though). I meant, I had this exact same question and as I was going to ask it, I saw your question. \$\endgroup\$
    – DMan
    Commented Nov 18, 2012 at 5:57
  • \$\begingroup\$ No worries. Jabs for not looking are common in comments, after all. \$\endgroup\$
    – ssb
    Commented Nov 18, 2012 at 5:59

2 Answers 2

1
\$\begingroup\$

OK, I've found an answer to my own question, however I'm not sure that it's the best answer. Basically the problem was in the order that the axes were checked which resulted in the hangup happening in some directions but not others.

So to resolve this I simply do the check one more time in reverse order. I was previously doing this recursively but not reversing the list. Here's the "working" code:

private void ResolvePlayer()
{
    List<ICollidable> reverse = obstacles;
    reverse.Reverse();

    foreach (CollidableRectangle obstacle in obstacles)
    {
        if (obstacle.ResolveCollision(player.Bounds) && obstacle.GetMTV() != Vector2.Zero)
        {
            player.Move(-obstacle.GetMTV());
            //ResolvePlayer();
            break;
        }
    }

    foreach (CollidableRectangle obstacle in reverse)
    {
        if (obstacle.ResolveCollision(player.Bounds) && obstacle.GetMTV() != Vector2.Zero)
        {
            player.Move(-obstacle.GetMTV());
            //ResolvePlayer();
            break;
        }
    }
}
\$\endgroup\$
3
  • \$\begingroup\$ However the lack of a recursive call means there's some wiggling around on collisions with rotated rectangles, so maybe this won't do. \$\endgroup\$
    – ssb
    Commented Nov 18, 2012 at 7:27
  • \$\begingroup\$ Reversing the list worked for me too! Oddly, I only had to reverse the list each frame (so it flips back and forth) and didn't have to resolve collisions again, though my system is probably different than yours. \$\endgroup\$
    – DMan
    Commented Nov 18, 2012 at 22:54
  • \$\begingroup\$ I can't help but feel like that would cause some minor hiccups when you approach something from the wrong direction on a particular frame, even if it switches the next. For example in implementing gravity, this could cancel a fall. \$\endgroup\$
    – ssb
    Commented Nov 19, 2012 at 3:36
0
\$\begingroup\$

After tinkering with this for a while, I've found a solution that works. Or rather, it works to solve this particular problem, and I don't know yet what other issues it causes. But at least for now it works perfectly.

To solve it I just iterated through each of the possible collisions, checked the translation against the others, and kept the vector with the greatest magnitude, or the successful resolution that resulted in the greatest distance.

private void ResolvePlayer()
{
    List<Vector2> possibleResolutions = new List<Vector2>();

    foreach (CollidableRectangle obstacle in obstacles)
    {
        if (obstacle.IsColliding(player.Bounds) && obstacle.GetMTV() != Vector2.Zero)
        {
            //player.Move(-obstacle.GetMTV());
            possibleResolutions.Add(-obstacle.GetMTV());
        }
    }

    Vector2 finalOffset = Vector2.Zero;
    float maxDistance = 0;
    foreach (Vector2 offset in possibleResolutions)
    {
        float thisDistance = Vector2.Distance(player.Position, player.Position + offset);
        if (thisDistance > maxDistance)
        {
            maxDistance = thisDistance;
            finalOffset = offset;
        }
    }

    player.Move(finalOffset);
}

This is what my resolution method looks like at the moment. I had a recursive call to handle jittering in corners but that gave me a stack overflow when testing against certain rotations, so for now I'm just resolving it twice. Fixing that bug shouldn't be too hard, though, and it can be possible to make it into one elegant method that handles everything.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .