🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

How can I fix this bug affecting my AABB function?

Started by
17 comments, last by Dirk Gregorius 5 years, 10 months ago

Simply put, my function compares two AABBs for collision detection, and when they are around the same size it works fine, but I noticed that if I greatly decreased the size of one of them (or augmented the size of the other), then the collision wouldn't be detected correctly; I would have to have them intersect for a collision to be registered rather than having it register when they are at least in direct contact, which is the intended behavior.

Below is my code.


local function DetectCollision(a, b) -- AABB to AABB
  local collisionX = (a.Position.X + a.Size.X) >= b.Position.X and (b.Position.X + b.Size.X) >= a.Position.X
  local collisionY = (a.Position.Y + a.Size.Y) >= b.Position.Y and (b.Position.Y + b.Size.Y) >= a.Position.Y
  local collisionZ = (a.Position.Z + a.Size.Z) >= b.Position.Z and (b.Position.Z + b.Size.Z) >= a.Position.Z
  return collisionX and collisionY and collisionZ
end

EDIT - To be more specific, the issues start to occur when I cut the size of one of the AABBs in half. For instance, if I had two cubes where one's size is 12 on all axes and the other is six on all axes, then the collision will not register. Upon debugging, I noticed that only one of the collision bools will become false. This seems to depend on what axis the smaller bounding box moves from in relation to the bigger one, so if I moved the smaller AABB away from the bigger one on the y-axis, then collisionY will be false.

I like matcha.

Advertisement

How much did you decrease the size of the AABB for it to stop working? Do you have an example that doesn't work? Did you try stepping through your code in a debugger and looking at the values of collisionX, collisionY and collisionZ for that example?

I don't really understand the logic you are using. It just seems completely off to me. 

Is position the center of a box or a corner?

@1024 Sorry about that, I should have included more information, the original post has been updated.

@Gnollrunner I am using the AABB function found here: https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection

Could you please explain how my logic is faulty?

I like matcha.

Actually in that case I think it should be OK.  I usually do it the other way around. 

57 minutes ago, Colin Jackman said:

@1024 Sorry about that, I should have included more information, the original post has been updated.

It would be great if you added even more information, the exact numbers in your example maybe. Are you sure that they collide? Maybe try to calculate the collision bools yourself and compare it with what the debugger does.

The logic looks right, so i guess maybe you use another convention when rendering, like using position as the center for visuals but top left for collisions.

Here is how I do AABB collision detection. My entities have a Scale factor (1.0 is normal size). This code is available on programming2dgames.com 

//=============================================================================
// Axis aligned bounding box collision detection method
// Called by collision()
// Pre:  &ent = The other Entity.
//       &collisionVector = Set by this function.
// Post: Returns true if collision, false otherwise.
//       Sets collisionVector if collision. The collisionVector points in the
//         direction of force that would be applied to this entity as a result
//         of the collision. The magnitude of the collision vector is the
//         distance the entities are overlapping.
//=============================================================================
bool Entity::collideBox(Entity &ent, VECTOR2 &collisionVector)
{
    // if either entity is not active then no collision may occcur
    if (!active || !ent.getActive())
        return false;

    // Check for collision using Axis Aligned Bounding Box
    if ((this->getCenterX() + edge.right*getScale() >= ent.getCenterX() + ent.getEdge().left*ent.getScale()) &&
        (this->getCenterX() + edge.left*getScale() <= ent.getCenterX() + ent.getEdge().right*ent.getScale()) &&
        (this->getCenterY() + edge.bottom*getScale() >= ent.getCenterY() + ent.getEdge().top*ent.getScale()) &&
        (this->getCenterY() + edge.top*getScale() <= ent.getCenterY() + ent.getEdge().bottom*ent.getScale()))
    {
        // If we get to here the entities are colliding. The edge with the smallest
        // overlapping section is the edge where the collision is occurring.
        // The collision vector is created perpendicular to the collision edge.

        // Calculate amount of overlap between entities on each edge of box
        float overlapX, overlapY;
        if (this->getCenterX() < ent.getCenterX())      // if this entity left of other entity
        {
            overlapX = (this->getCenterX() + edge.right*getScale()) - (ent.getCenterX() + ent.getEdge().left*ent.getScale());
            collisionVector = VECTOR2(-overlapX, 0);    // collison vector points left
        }
        else    // this entity right of other entity
        {
            overlapX = (ent.getCenterX() + ent.getEdge().right*ent.getScale()) - (this->getCenterX() + edge.left*getScale());
            collisionVector = VECTOR2(overlapX, 0);     // collison vector points right
        }
        if (this->getCenterY() < ent.getCenterY())      // if this entity above other entity
        {
            overlapY = (this->getCenterY() + edge.bottom*getScale()) - (ent.getCenterY() + ent.getEdge().top*ent.getScale());
            if (overlapY < overlapX)                    // if Y overlap is smaller
                collisionVector = VECTOR2(0, -overlapY); // collison vector points up
        }
        else    // this entity below other entity
        {
            overlapY = (ent.getCenterY() + ent.getEdge().bottom*ent.getScale()) - (this->getCenterY() + edge.top*getScale());
            if (overlapY < overlapX)                    // if Y overlap is smaller
                collisionVector = VECTOR2(0, overlapY); // collison vector points down
        }

        return true;    // entities are colliding
    }
    return false;       // entities are not colliding
}
 

Why mandate doing the test on all three axes every time?  Why not have nested conditionals so that if the collision check along the x-axis fails you can abandon the checks on y and z and return a false.  That way you can fail quickly and speed up the algorithm.  ...or am I missing something here.  I'll admit, I'm not a computer scientist, I'm just a scientist who owns a computer and I understand the difference is profound.  It just seems to me that if one axis fails to collide, there's no point in checking the others.

33 minutes ago, Doug Patterson said:

Why mandate doing the test on all three axes every time?  Why not have nested conditionals so that if the collision check along the x-axis fails you can abandon the checks on y and z and return a false.

I second that :)

Also a very simple method that could work:

if ((abs(a.pos.x - b.pos.x) < a.size.x + b.size.x) && abs(a.pos.y - b.pos.y) < a.size.y + b.size.y) && abs(a.pos.z - b.pos.z) < a.size.z + b.size.z)) { ... you have collision ... }

So the method is just like with circles or spheres, get the distance of their centers and if it's smaller than the sum of their sizes then they collide.
With boxes you have to do 3 separate checks for each axis, but the principle is the same.

This topic is closed to new replies.

Advertisement