- Home /
Problems implementing rotated Rect to Rect collision detection
I've been trying to implement rectangle on rectangle collision detection on the XZ plane. No assumptions on the positions, rotations and sizes of the rectangles can be made, so I've chosen to follow this guide, which is based on the separating axis theorem, in order to cover all possible scenarios.
This implementation detects all collisions correctly, but it also returns false positives when one or both rectangles are rotated. Judging by which configurations are considered to be colliding and which not, it feels like each object behaves as if it had 2 colliders, one being the correctly rotated one and the other being axis-aligned (Thus the lack of problems when the rectangles are axis-aligned to begin with).
The (relevant) code is as follows:
public class Ground2DCollider : MonoBehaviour{
public Vector2 rectangleSize;
//Other stuff
}
public static bool CheckCollisions(Ground2DCollider toCheck, out Ground2DCollider collider){
List<Ground2DCollider> potentialCollisions = new List<Ground2DCollider>();
//First pass to determine which rectangles could be colliding. potentialCollisions is set during this
//Variables are named as follows: AtL = [Rectangle] [top || bottom] [Left || Right] = top-Left corner of rectangle A
Vector2 AtL = ToGroundV2(toCheck.transform.position + toCheck.transform.rotation * new Vector3(-0.5f * toCheck.rectangleSize.x, 0f, 0.5f * toCheck.rectangleSize.y));
Vector2 AtR = ToGroundV2(toCheck.transform.position + toCheck.transform.rotation * new Vector3(0.5f * toCheck.rectangleSize.x, 0f, 0.5f * toCheck.rectangleSize.y));
Vector2 AbL = ToGroundV2(toCheck.transform.position + toCheck.transform.rotation * new Vector3(-0.5f * toCheck.rectangleSize.x, 0f, -0.5f * toCheck.rectangleSize.y));
Vector2 AbR = ToGroundV2(toCheck.transform.position + toCheck.transform.rotation * new Vector3(0.5f * toCheck.rectangleSize.x, 0f, -0.5f * toCheck.rectangleSize.y));
foreach(Ground2DCollider coll in potentialCollisions){
Vector2 BtL = ToGroundV2(coll.transform.position + coll.transform.rotation * new Vector3(-0.5f * coll.rectangleSize.x, 0f, 0.5f * coll.rectangleSize.y));
Vector2 BtR = ToGroundV2(coll.transform.position + coll.transform.rotation * new Vector3(0.5f * coll.rectangleSize.x, 0f, 0.5f * coll.rectangleSize.y));
Vector2 BbL = ToGroundV2(coll.transform.position + coll.transform.rotation * new Vector3(-0.5f * coll.rectangleSize.x, 0f, -0.5f * coll.rectangleSize.y));
Vector2 BbR = ToGroundV2(coll.transform.position + coll.transform.rotation * new Vector3(0.5f * coll.rectangleSize.x, 0f, -0.5f * coll.rectangleSize.y));
Vector2 axis1 = AtR - AtL;
Vector2 axis2 = AtR - AbR;
Vector2 axis3 = BtL - BbL;
Vector2 axis4 = BtL - BtR;
if(!CheckAxis(axis1, AtL, AtR, AbL, AbR, BtL, BtR, BbL, BbR)){
continue;
}
if(!CheckAxis(axis2, AtL, AtR, AbL, AbR, BtL, BtR, BbL, BbR)){
continue;
}
if(!CheckAxis(axis3, AtL, AtR, AbL, AbR, BtL, BtR, BbL, BbR)){
continue;
}
if(!CheckAxis(axis4, AtL, AtR, AbL, AbR, BtL, BtR, BbL, BbR)){
continue;
}
collider = coll;
return true;
}
collider = null;
return false;
}
private static bool CheckAxis(Vector2 axis, Vector2 AtL, Vector2 AtR, Vector2 AbL, Vector2 AbR, Vector2 BtL, Vector2 BtR, Vector2 BbL, Vector2 BbR){
float dAtL = Vector2.Dot(axis, ProjectToAxis(axis, AtL));
float dAtR = Vector2.Dot(axis, ProjectToAxis(axis, AtR));
float dAbL = Vector2.Dot(axis, ProjectToAxis(axis, AbL));
float dAbR = Vector2.Dot(axis, ProjectToAxis(axis, AbR));
float dBtL = Vector2.Dot(axis, ProjectToAxis(axis, BtL));
float dBtR = Vector2.Dot(axis, ProjectToAxis(axis, BtR));
float dBbL = Vector2.Dot(axis, ProjectToAxis(axis, BbL));
float dBbR = Vector2.Dot(axis, ProjectToAxis(axis, BbR));
float minA = Mathf.Min(Mathf.Min(Mathf.Min(dAtL, dAtR), dAbL), dAbR);
float maxA = Mathf.Max(Mathf.Max(Mathf.Max(dAtL, dAtR), dAbL), dAbR);
float minB = Mathf.Min(Mathf.Min(Mathf.Min(dBtL, dBtR), dBbL), dBbR);
float maxB = Mathf.Max(Mathf.Max(Mathf.Max(dBtL, dBtR), dBbL), dBbR);
if(minB <= maxA && maxB >= minA){
return true;
}
return false;
}
private static Vector2 ProjectToAxis(Vector2 axis, Vector2 point){
float multp = point.x * axis.x + point.y * axis.y / (axis.x * axis.x + axis.y * axis.y);
return multp * axis;
}
private static Vector2 ToGroundV2(Vector3 point){
return new Vector2(point.x, point.z);
}
I've checked the code, but have been unable to find any mistakes. Thus I wanted to ask whether there are any more fundamental problems with how it's been implemented.
If there's any part of the code that's unclear or confusing, feel free to ask.