lua 碰撞检测

不规则图形碰撞检测

对于矩形碰撞,很多人都知道。但面对多边形图形,大多数采用多矩形覆盖的方式。
但是我不是很喜欢这种方式,我所采用的是利用一个经典算法:
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;//表示知道未碰撞
}


【简单碰撞检测】

    在一些游戏中经常会遇到碰撞检测的情况,如愤怒的小鸟飞出去后,是否与石头发生碰撞。

    虽然说有一个Box2D物理碰撞引擎,但是在这里还是需要掌握一下简单的碰撞检测方法。

    (1)矩形与矩形

    (2)圆与圆

    (3)矩形与圆


1、矩形与矩形


    1.1、提出问题

    问题:假设有两个矩形rect1,rect2,判断两矩是否碰撞相交(部分区域重叠)。

    如下四幅图中,图1、2、4发生碰撞,图3未发生碰撞。

wKiom1QFwuCS0a7IAAAnXuLCBYM543.jpg    wKioL1QFwuKQ7lK2AAAxXV3oZ_k652.jpg

wKiom1QFwuCBbwtqAAAkm2811VU152.jpg    wKioL1QF0Ojz-1qnAAAh5ghkGt8517.jpg


    1.2、解决方案

    由图可知,判断方法只要计算一下两个矩形相交部分是否能够成一个小矩形。

    判断方法如下:(可用于计算相交部分的小矩形信息)

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;  //发生碰撞
    }
//


    当然也可以使用cocos2dx引擎中的CCRect类已经存在的一个判断矩形碰撞的函数。

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());
    }
//


2、圆与圆

    2.1、提出问题

    问题:假设有两个圆circle1,circle2,判断两圆是否碰撞相交(部分区域重叠)。

    如下三幅图中,图1、2发生碰撞,图3未发生碰撞。

wKioL1QF06Xgh6yhAABYDrSfp5g151.jpg    wKiom1QF06OQN5SrAABV2uYrcxg422.jpg

wKioL1QF06Wzz1RTAABSejZG4UM796.jpg

    

    2.2、解决方案

    圆的碰撞检测比较简单,只要判断两圆心距离是否小于半径相加(r1+r2)即可。

    判断方法如下:

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) ;
    }
//


3、矩形与圆

    3.1、提出问题

    问题:假设有矩形rect、圆circle,判断矩形和圆是否碰撞相交(部分区域重叠)。

    如下四幅图中,图1、2、4发生碰撞,图3未发生碰撞。

wKioL1QF73nAZKVTAAA6sBYX2Rs665.jpg    wKiom1QF73eQvB-bAAA4x3hbNkA813.jpg

wKioL1QF73miRhBnAAAxMVKieIM009.jpg        wKiom1QF73iyAqc2AABSjhSEcrE580.jpg

    

    3.2、解决方案

    矩形和圆的判断比较复杂,请看以下分析。

    (1)首先,我们让圆在矩形外沿着矩形的边滚一圈,然后将圆心移动的轨迹连线,就可以得到一个圆角矩形。

    (2)如下图红色区域为圆角矩形,显然我们只要判断圆心是否在圆角矩形区域内部即可。

wKioL1QF9iSgsP6CAADe5iZ6hoA754.jpg    

    (3)如果除去圆角矩形四个角上的4个四分之一圆的部分,仅仅让你判断圆心是否落在剩下的区域内,你应该能很快想出解决办法吧?

        只要判断圆心是否在两个矩形的任意其中之一的内部即可。

wKioL1QF-Q3BIL1ZAACBVOfs2_c233.jpg

    (4)然后再判断圆心是否在四个角上的四分之一圆的区域部分即可。

        显然,只要判断圆心与矩形的四个顶点的距离是否小于圆的半径即可。

    (5)综合上诉:(3)(4)的判断,即可得出圆是否矩形相交。


    判断方法如下:

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
    }
//

阅读更多
文章标签: 游戏
个人分类: Lua
上一篇lua函数详解
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭