0
\$\begingroup\$

I have a problem with AABB collision.

I am making a top down rougelike in SFML and my collision algorithm does't quite work properly.

HERE IS A GIF OF THE PROBLEM.

It mostly works, except for when you're against the corner of the block and hold the direction towards the block for a second. For some reason, after about a second, the player is clipped through the block to the adjacent side.

I think the problem is caused when, for example, the x axis needs to be resolved as the player is approaching the object from the left, but the y penetration ends up being more than the x and thus the collision is resolved incorrectly.

This is my collision code:

sf::FloatRect collisionRect;
std::vector<Entity*> activeEntities;
for each (Entity* e in entities)
    if (e->otype == Dynamic)
        activeEntities.push_back(e);
for each (Entity* dynamicEntity in activeEntities)
{
    for each (Entity* target in entities)
    {
        if (dynamicEntity->sprite.getGlobalBounds().intersects(target->sprite.getGlobalBounds(), collisionRect) && dynamicEntity != target)
        {
            dynamicEntity->velocity = sf::Vector2f(0, 0);
            if (collisionRect.width > collisionRect.height)
            {
                if (collisionRect.contains(collisionRect.left, dynamicEntity->sprite.getPosition().y))
                    dynamicEntity->sprite.setPosition(dynamicEntity->sprite.getPosition().x, dynamicEntity->sprite.getPosition().y + collisionRect.height);
                else
                    dynamicEntity->sprite.setPosition(dynamicEntity->sprite.getPosition().x, dynamicEntity->sprite.getPosition().y - collisionRect.height);
            }
            else if (collisionRect.height > collisionRect.width)
            {
                if (collisionRect.contains(dynamicEntity->sprite.getPosition().x + dynamicEntity->sprite.getGlobalBounds().width - 1.f, collisionRect.top + 1.f ))
                    dynamicEntity->sprite.setPosition(dynamicEntity->sprite.getPosition().x - collisionRect.width, dynamicEntity->sprite.getPosition().y);
                else
                    dynamicEntity->sprite.setPosition(dynamicEntity->sprite.getPosition().x + collisionRect.width, dynamicEntity->sprite.getPosition().y);
            }
        }
    }
}

EDIT: I have tried making only one call to getPosition() getSprite() etc., and that only makes it glitchier.

EDIT2: I made a gfy of the problem. Sorry for the low res.

\$\endgroup\$
  • \$\begingroup\$ You seem to be missing a clause where collisionRect.width == collisionRect.height, is this intentional? \$\endgroup\$ – Niels Feb 23 '16 at 13:34
  • \$\begingroup\$ Also, since it apparently onyl happes when approaching from the left, you could set the breakpoint in that call to see what values are involved at the point where things go wrong. \$\endgroup\$ – Niels Feb 23 '16 at 13:41
  • \$\begingroup\$ Sorry if my helping pictures led you astray, but it happens from every side. \$\endgroup\$ – omni Feb 23 '16 at 18:30
1
+50
\$\begingroup\$

This is actually a pretty common problem. You are correct in your guess that it evaluates the y collision first since your physics or what not push you vertically into the block quicker than your horizontal movement.

Currently you evaluate the greater collision first. You shouldn't do this. Instead, evaluate the lesser of the two collisions first. i.e. if you jump into a wall and your jump speed is faster than your horizontal movement speed, it will evaluate the horizontal collision with the wall first instead of the the vertical jump.

I'll be honest, I didn't bother to read all your code as this issue is so common. Feel free to comment if my answer doesn't work for your specific needs and I'll do a better job reading.

EDIT: So I took a look at your code and it seems like you do the following:

1) Check for the intersections of the AABB's and store that collision rectangle in collisionRect.

2) Set velocity equal to zero.

3) Check if the width of the bounding box is greater than the height. This essentially checks if the collision is a y-axis collision.

4) If it is a y-axis collision, check to see if collisionRectcontains the top of the sprite. This essentially checks if a collision has occurred at the top of the sprite.

5) If a collision has occurred at the top of the sprite, then set the sprite's position to the top of the collisionRect (put it on top of the thing you collided with).

And then repeat steps 3, 4, and 5 with an x-axis collision and right and left instead of top and bottom.

EDIT: So as crazy as this sounds, I believe that your bug occurs when collisionRect is exactly a square (when the height is equal to the width). This would cause the freezing since your code doesn't account for this situation. It would also make sense that it would evaluate the y-axis collision first simply because that if your first check (the if-statement). Off the top of my head you have two options if this is the case: evaluate either the x or the y collision first before the other, or see if velocity is greater in the x or y direction and resolve that collision first (since that's the direction you're moving in). Obviously the second solution would require you not set velocity equal to zero before the checks. I would suggest setting velocity to zero inside to if and else if statements.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the answer. Should I be setting velocity to 0 when a collision is detected like I'm doing? \$\endgroup\$ – omni Feb 23 '16 at 2:31
  • \$\begingroup\$ Also, I've already seen all the things you've linked. None of them apply to my specific problem. \$\endgroup\$ – omni Feb 23 '16 at 2:38
  • \$\begingroup\$ @omni I have updated my answer by the way. I looked at your code for a while and this is the only thing I could come up with. Let me know if it works. If not, then I'll get back to the drawing board haha \$\endgroup\$ – bpgeck Feb 23 '16 at 18:04
  • \$\begingroup\$ Yeah sorry, changing what you recommended didn't fix the bug and introduced a new one. Thank you for helping though. I don't think the problem arises when the width and height of collisionRect are equal; instead, I think it;s a problem with phasing through the block in between frames, as the bug only occurs when you hold down the direction towards the block. \$\endgroup\$ – omni Feb 23 '16 at 18:36
  • 1
    \$\begingroup\$ gfycat.com/SaneGlamorousFalcon heres a gif if it helps \$\endgroup\$ – omni Feb 26 '16 at 7:56

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.