不规则图形碰撞检测
对于矩形碰撞,很多人都知道。但面对多边形图形,大多数采用多矩形覆盖的方式。
但是我不是很喜欢这种方式,我所采用的是利用一个经典算法:
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; // 表示知道未碰撞
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//
bool
collision_RectWithRect(CCRect rect1, CCRect rect2)
{
//计算相交部分的矩形
//左下角坐标:( lx , ly )
//右上角坐标:( rx , ry )
float
lx = max(rect1.getMinX() , rect2.getMinX() );
float
ly = max(rect1.getMinY() , rect2.getMinY() );
float
rx = min(rect1.getMaxX() , rect2.getMaxX() );
float
ry = min(rect1.getMaxY() , rect2.getMaxY() );
//判断是否能构成小矩形
if
( lx > rx || ly > ry )
return
false
;
//矩形不相交
else
return
true
;
//发生碰撞
}
//
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//
//返回bool。相交为true
rect1.intersectsRect(rect2);
//
//
//intersectsRect()函数的源码如下:
bool
CCRect::intersectsRect(
const
CCRect& rect)
const
{
return
!( getMaxX() < rect.getMinX() ||
rect.getMaxX() < getMinX() ||
getMaxY() < rect.getMinY() ||
rect.getMaxY() < getMinY());
}
//
|
1
2
3
4
5
6
7
8
9
10
|
//
bool
collision_CircleWithCircle(CCPoint p1,
float
r1, CCPoint p2,
float
r2)
{
//计算圆心距离
float
dist = p1.getDistance(p2);
//判断两圆是否相交
return
dist < (r1+r2) ;
}
//
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
//
bool
collision_RectWithCircle(CCRect rect, CCPoint p,
float
r)
{
//获取矩形信息
//左下角坐标:( lx , ly )
//右上角坐标:( rx , ry )
float
lx = rect.getMinX();
float
ly = rect.getMinY();
float
rx = rect.getMaxX();
float
ry = rect.getMaxY();
//计算圆心到四个顶点的距离
float
d1 = p.getDistance( ccp(lx, ly) );
float
d2 = p.getDistance( ccp(lx, ry) );
float
d3 = p.getDistance( ccp(rx, ly) );
float
d4 = p.getDistance( ccp(rx, ry) );
//判断是否碰撞
//判断距离是否小于半径
if
( d1<r || d2<r || d3<r || d4<r )
return
true
;
//是否在圆角矩形的,横向矩形内
if
( p.x > (lx-r) && p.x < (rx+r) && p.y > ly && p.y < ry )
return
true
;
//是否在圆角矩形的,纵向矩形内
if
( p.x > lx && p.x < rx && p.y > (ly-r) && p.y < (ry+r) )
return
true
;
//不发生碰撞
return
false
;
}
//
|