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.
As you can see the bottom right corner is intersecting the other box, but isn't colliding.
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;
}
No comments:
Post a Comment