1
\$\begingroup\$

I am trying to create a 2d top down shooter using DirectX 11 (Windows 8 Store) and I am trying to implement OOBB collision using the Separating Axis Theorem.

However I appear to have run into an issue which I cannot fathom.

I will upload a few images to aid my explanation. The boxes only collide when the top left or top right corner intersect the other collision object but does not work if the bottom corners collide with it.

When the boxes are AABB they collide perfectly from all directions.

No collision from bottom corners

As you can see the bottom right corner is intersecting the other box, but isn't colliding.

Collision from top corners

The rotation origin (left corner) is intersecting the other box and is resulting in a collision correctly. This appears to be the same for the top right corner.

Code:

void Entity::computeRotatedBox()

{

/*if(rotatedBoxReady)
    return;*/

getPosition();
float projection;


float rot = rotation;

Vector2 rotatedX(cos(rot), sin(rot));
Vector2 rotatedY(-sin(rot), cos(rot));


const Vector2 *center = getCenter();

//Calculate corner position relative to rotation
corners[0] = *center + rotatedX * ((float)edge.left) + rotatedY * ((float)edge.top);
corners[1] = *center + rotatedX * ((float)edge.right) + rotatedY * ((float)edge.top);
corners[2] = *center + rotatedX * ((float)edge.right) + rotatedY * ((float)edge.bottom);
corners[3] = *center + rotatedX * ((float)edge.left) + rotatedY * ((float)edge.bottom);


edge01 = Vector2(corners[1].x - corners[0].x, corners[1].y - corners[0].y);
Vector2Normalize(&edge01);
edge03 = Vector2(corners[3].x - corners[0].x, corners[3].y - corners[0].y);
Vector2Normalize(&edge03);

//Calculate projection onto line
projection = Vector2Dot(&edge01, &corners[0]);
entA01min = projection;
entA01max = projection;

//Project onto Axis 1
projection = Vector2Dot(&edge01, &corners[1]);
if(projection < entA01min)
    entA01min = projection;
else if(projection > entA01max)
    entA01max = projection;

//Project onto "Axis 3"
projection = Vector2Dot(&edge03, &corners[0]);
entA03min = projection;
entA03max = projection;
projection = Vector2Dot(&edge03, &corners[3]);
if(projection < entA03min)
    entA03min = projection;
else if(projection > entA03max)
    entA03max = projection;

rotatedBoxReady = true;

}

bool Entity::projectionOverlap(Entity &ent, Vector2 &collisionVector) { float projection;

projection = Vector2Dot(&edge01, ent.getCorner(0));

//Project Corner 0
entB01min = projection;
entB01max = projection;

for(int c = 1; c<4; c++)
{
    projection = Vector2Dot(&edge01, ent.getCorner(c));
    if(projection < entB01min)
        entB01min = projection;
    else if(projection > entB01max)
        entB01max = projection;
}

if(entB01min > entA01max || entB01max < entA01min)
    return false;

//Project other onto axis 3
projection = Vector2Dot(&edge03, ent.getCorner(0));
entB03min = projection;
entB03max = projection;

for(int c = 1; c<4; c++)
{
    projection = Vector2Dot(&edge03, ent.getCorner(c));
    if(projection < entB03min)
        entB03min = projection;
    else if(projection > entB03max)
        entB03max = projection;
}

//If there is no overlap
if(entB03min > entA03max || entB03max < entA03min)
    return false;

return true; //Otherwise they must overlap.

}

Vector2 * Entity::getCorner(UINT c) { if(c>= 4) c = 0;

return &corners[c];

}

bool Entity::collideRotatedBox(Entity &ent, DirectX::SimpleMath::Vector2 &collisionVector) {

float overlap01, overlap03;

computeRotatedBox();        //Prepare rotated boxes
ent.computeRotatedBox();    //Prepare rotated box

if(projectionOverlap(ent, collisionVector) && ent.projectionOverlap(*this, collisionVector))
{

    if(entA01min < entB01min)
    {
        overlap01 = entB01max - entB01min;
        collisionVector = corners[1] - corners[0];
    }
    else    // else, A right of B
    {
        overlap01 = entB01max - entA01min;
        collisionVector = corners[0] - corners[1];
    }
    if (entA03min < entB03min)   // if A above B
    {
        overlap03 = entA03max - entB03min;
        if (overlap03 < overlap01)
            collisionVector = corners[3] - corners[0];
    }
    else    // else, A below B
    {
        overlap03 = entB03max - entA03min;
        if (overlap03 < overlap01)
            collisionVector = corners[0] - corners[3];
    }
    return true;

}
return false;

}

\$\endgroup\$
  • \$\begingroup\$ I think it's impossible to answer this without seeing some code. \$\endgroup\$ – GuyRT Apr 2 '14 at 14:10
  • \$\begingroup\$ I've added the code which computes the rotated boxes and the overlaps. Sorry about that ;D \$\endgroup\$ – Nick Apr 2 '14 at 14:32
  • \$\begingroup\$ Did you use exactly the same corners array to draw your image as you use in your collision code? \$\endgroup\$ – GuyRT Apr 2 '14 at 16:38
  • \$\begingroup\$ It's helpful to add a rendering mode that visually shows each of the separating axes one by one, and the calculated box projections along them. That way you can see directly what part of the process is going wrong. \$\endgroup\$ – Nathan Reed Apr 2 '14 at 16:44
  • \$\begingroup\$ The sprite is drawn according to its x,y position and then rotated by SpriteBatch \$\endgroup\$ – Nick Apr 2 '14 at 16:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.