1
\$\begingroup\$

Hello I am trying to handle some simple box collision (axis-aligned) in my top-down 2d game, but running into a problem.

My game is running at a fixed time step, and checks for player-wall collision on each iteration. It retrieves some collision data (if it collided & what side of the bounding box) and adds some offset (dir) to the players position depending on the overlap amount.

// Check player with walls
        CollisionData col = testBoxCollision(m_player->getCollider(), wall.getCollider());
        if (col.isCollided){
            Vec2f dir(0.0f, 0.0f);
            if (col.left)
                dir.x -= (m_player->getCollider().getPosition().x + m_player->getCollider().getHalfSize().x) - (wall.getPosition().x - wall.getHalfSize().x);
            else if (col.right)
                dir.x += (wall.getPosition().x + wall.getHalfSize().x) - (m_player->getCollider().getPosition().x - m_player->getCollider().getHalfSize().x);
            if (col.top)
                dir.y -= (m_player->getCollider().getPosition().y + m_player->getCollider().getHalfSize().y) - (wall.getPosition().y - wall.getHalfSize().y);
            else if (col.bottom)
                dir.y += (wall.getPosition().y + wall.getHalfSize().y) - (m_player->getCollider().getPosition().y - m_player->getCollider().getHalfSize().y);

            m_player->GameObject::setPosition(m_player->GameObject::getPosition() + dir);
        }

The testBoxCollision function:

CollisionData testBoxCollision(const Collider& a, const Collider& b)

{ CollisionData c{ false, false, false, false, false };

float w  = a.getHalfSize().x + b.getHalfSize().x;
float h  = a.getHalfSize().y + b.getHalfSize().y;
float dx = a.getPosition().x - b.getPosition().x;
float dy = a.getPosition().y - b.getPosition().y;

if (abs(dx) <= w && abs(dy) <= h)
{
    c.isCollided = true;

    float wy = w * dy;
    float hx = h * dx;

    if (wy > hx)
    {
        if (wy > -hx)
            c.bottom = true;
        else
            c.left = true;
    }
    else
    {
        if (wy > -hx)
            c.right = true;
        else
            c.top = true;
    }
}
return c;

}

The Problem

When two walls produce a corner, and I push my player up to the corner, the player goes through the wall. Not Ideal!

Clearly I've overlooked something and another set of eyes would be very helpful. Thanks.

Below is a snapshot of what's happening.

enter image description here

EDIT: I'm led to believe this is a math error, because it only happens on some corners at a consistent frame rate.

\$\endgroup\$
4
  • \$\begingroup\$ You might check and see if this only happens when you are in negative coordinates \$\endgroup\$
    – Alan Wolfe
    Apr 4, 2015 at 17:08
  • \$\begingroup\$ Hi Alan, thanks for replying. I have checked and it seems to be happening in positive coordinates. I'm completely puzzled to why this is happening. \$\endgroup\$
    – Jack H
    Apr 4, 2015 at 18:35
  • \$\begingroup\$ Are you checking each wall? In what order? \$\endgroup\$
    – Mutoh
    Apr 5, 2015 at 3:04
  • \$\begingroup\$ Try multiplying dir by 1.1f. This may be too far, but verify that you no longer slide through. \$\endgroup\$
    – Jon
    Apr 5, 2015 at 3:20

3 Answers 3

1
\$\begingroup\$

A possible explanation for this is unrelated to the corners: to determine whether it's colliding left or right, or if it's colliding up or down, instead of checking how far inside the player's hitbox is within the wall you should check the direction of the player's movement.

It would go more along these lines:

    CollisionData testPlayerCollision(const Player& player, const Wall& wall) {
        CollisionData c{ false, false, false, false, false };

        if (player.getCollider().intersects(wall.getCollider())) {
            c.isCollided = true;

            if (player.isMovingRight()) {
                c.left = true;
            }
            else c.right = true;

            if (player.isMovingDown()) {
                c.up = true;
            }
            else c.down = true;
        }

        return c;
    }
\$\endgroup\$
1
\$\begingroup\$

Ok so I found the solution, and thanks for you replies.

The linem_player->GameObject::setPosition(m_player->GameObject::getPosition() + dir); was only updating the gameobjects position variable, this variable only took effect on the gameobjects update. Which why of course it worked when there was only one wall. But with a second wall, it would override the objects new position determined by the first wall. So I simply update the players position immediately.

\$\endgroup\$
-1
\$\begingroup\$
if (col.left)
   dir.x += (wall.getPosition().x + wall.getHalfSize().x)
   - (m_player->getCollider().getPosition().x - m_player->getCollider().getHalfSize().x);
else if (col.right)
   dir.x -= (m_player->getCollider().getPosition().x + m_player->getCollider().getHalfSize().x)
   - (wall.getPosition().x - wall.getHalfSize().x);
\$\endgroup\$

You must log in to answer this question.

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