對於矩形碰撞,很多人都知道。但面對多邊形圖形,大多數采用多矩形覆蓋的方式。
但是我不是很喜歡這種方式,我所采用的是利用一個經典算法:
SAT 一種可以快速檢測不規則的凸多邊形是否碰撞的算法
給出兩個凸多邊形體,如果我們能找到一個軸線,使兩物體在此軸線上的投影不重疊,則這兩個物體之間沒有發生碰撞,這個軸線叫做Separating Axis(紅色軸線)。
對於2D來說,紅色線就是垂直與多邊形邊的軸。
因此,如果我們要檢查兩多邊形是否碰撞,就去檢查兩多邊形在每個所有可能的軸上的投影是否重疊。
/// <summary>
///
檢測2個矩形是否發生碰撞
/// </summary>
/// <returns></returns>
public static bool IsIntersect (Vector2[] A, Vector2[] B)
{
Vector2 AX, AY, BX, BY;
AX = new Vector2();
AY = new Vector2();
BX = new Vector2();
BY = new Vector2();
AX.X = A[ 0].X - A[ 1].X;
AX.Y = A[ 0].Y - A[ 1].Y;
AY.X = A[ 0].X - A[ 3].X;
AY.Y = A[ 0].Y - A[ 3].Y;
BX.X = B[ 0].X - B[ 1].X;
BX.Y = B[ 0].Y - B[ 1].Y;
BY.X = B[ 0].X - B[ 3].X;
BY.Y = B[ 0].Y - B[ 3].Y;
// 對於AX上:
if (Tmp(AX, A, B)) return false;
if (Tmp(AY, A, B)) return false;
if (Tmp(BX, A, B)) return false;
if (Tmp(BY, A, B)) return false;
return true;
}
private static bool Tmp(Vector2 IS,Vector2[] A,Vector2[] B)
{
float[] v = new float[ 4];
for ( int i = 0; i < 4; i++)
{
float tmp = (IS.X * A[i].X + IS.Y * A[i].Y) / (IS.X * IS.X + IS.Y * IS.Y);
v[i] = tmp * IS.X * IS.X + tmp * IS.Y * IS.Y;
}
float[] vv = new float[ 4];
for ( int i = 0; i < 4; i++)
{
float tmp = (IS.X * B[i].X + IS.Y * B[i].Y) / (IS.X * IS.X + IS.Y * IS.Y);
vv[i] = tmp * IS.X * IS.X + tmp * IS.Y * IS.Y;
}
if (Math.Max(Math.Max(v[ 0], v[ 1]),Math.Max(v[ 2],v[ 3])) >Math.Min(Math.Min(vv[ 0],vv[ 1]),Math.Min(vv[ 2],vv[ 3])) && Math.Min(Math.Min(v[ 0],v[ 1]),Math.Min(v[ 2],v[ 3])) < Math.Max(Math.Max(vv[ 0],vv[ 1]),Math.Max(vv[ 2],vv[ 3]))) {
return false;
} // 表示暫時不知道是否碰撞
else return true; // 表示知道未碰撞
}
/// </summary>
/// <returns></returns>
public static bool IsIntersect (Vector2[] A, Vector2[] B)
{
Vector2 AX, AY, BX, BY;
AX = new Vector2();
AY = new Vector2();
BX = new Vector2();
BY = new Vector2();
AX.X = A[ 0].X - A[ 1].X;
AX.Y = A[ 0].Y - A[ 1].Y;
AY.X = A[ 0].X - A[ 3].X;
AY.Y = A[ 0].Y - A[ 3].Y;
BX.X = B[ 0].X - B[ 1].X;
BX.Y = B[ 0].Y - B[ 1].Y;
BY.X = B[ 0].X - B[ 3].X;
BY.Y = B[ 0].Y - B[ 3].Y;
// 對於AX上:
if (Tmp(AX, A, B)) return false;
if (Tmp(AY, A, B)) return false;
if (Tmp(BX, A, B)) return false;
if (Tmp(BY, A, B)) return false;
return true;
}
private static bool Tmp(Vector2 IS,Vector2[] A,Vector2[] B)
{
float[] v = new float[ 4];
for ( int i = 0; i < 4; i++)
{
float tmp = (IS.X * A[i].X + IS.Y * A[i].Y) / (IS.X * IS.X + IS.Y * IS.Y);
v[i] = tmp * IS.X * IS.X + tmp * IS.Y * IS.Y;
}
float[] vv = new float[ 4];
for ( int i = 0; i < 4; i++)
{
float tmp = (IS.X * B[i].X + IS.Y * B[i].Y) / (IS.X * IS.X + IS.Y * IS.Y);
vv[i] = tmp * IS.X * IS.X + tmp * IS.Y * IS.Y;
}
if (Math.Max(Math.Max(v[ 0], v[ 1]),Math.Max(v[ 2],v[ 3])) >Math.Min(Math.Min(vv[ 0],vv[ 1]),Math.Min(vv[ 2],vv[ 3])) && Math.Min(Math.Min(v[ 0],v[ 1]),Math.Min(v[ 2],v[ 3])) < Math.Max(Math.Max(vv[ 0],vv[ 1]),Math.Max(vv[ 2],vv[ 3]))) {
return false;
} // 表示暫時不知道是否碰撞
else return true; // 表示知道未碰撞
}
其中需要的參數為矩形的四個點坐標(一共八個點),矩形四個點坐標要求按順序(順時針或者逆時針都可以)傳入。(因為項目中不涉及超過四個以上頂點的檢測,所以只實現了矩形的檢測。)
