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

在Love2d中进行碰撞检测,可以使用内置的碰撞检测函数`love.physics`和`love.math`。 1. 使用love.physics进行碰撞检测 首先需要创建一个物理世界,然后在物理世界中添加物体(包括形状、质量等),最后在`love.update`函数中调用`world:update(dt)`,更新物理世界中物体的位置和状态。在物理世界中,可以使用`contact`事件检测两个物体是否发生碰撞。 示例代码: ```lua function love.load() love.physics.setMeter(64) world = love.physics.newWorld(0, 9.81*64, true) objects = {} objects.ground = {} objects.ground.body = love.physics.newBody(world, 650/2, 650-50/2) objects.ground.shape = love.physics.newRectangleShape(650, 50) objects.ground.fixture = love.physics.newFixture(objects.ground.body, objects.ground.shape) objects.ball = {} objects.ball.body = love.physics.newBody(world, 650/2, 650/2, "dynamic") objects.ball.shape = love.physics.newCircleShape(20) objects.ball.fixture = love.physics.newFixture(objects.ball.body, objects.ball.shape, 1) objects.ball.fixture:setRestitution(0.9) objects.ball.fixture:setFriction(0.1) end function love.update(dt) world:update(dt) end function love.draw() love.graphics.polygon("line", objects.ground.body:getWorldPoints(objects.ground.shape:getPoints())) love.graphics.circle("line", objects.ball.body:getX(), objects.ball.body:getY(), objects.ball.shape:getRadius()) end function love.keypressed(key) if key == "space" then objects.ball.body:applyLinearImpulse(0, -200) end end function beginContact(a, b, coll) -- 碰撞开始时的处理 end function endContact(a, b, coll) -- 碰撞结束时的处理 end function preSolve(a, b, coll) -- 碰撞预处理 end function postSolve(a, b, coll, normalimpulse, tangentimpulse) -- 碰撞后处理 end world:setCallbacks(beginContact, endContact, preSolve, postSolve) ``` 2. 使用love.math进行碰撞检测 可以使用`love.math.intersectSegments`函数判断两条线段是否相交。通过判断物体的边界矩形是否相交,可以进行简单的碰撞检测。 示例代码: ```lua function love.load() object1 = {x = 100, y = 100, w = 50, h = 50} object2 = {x = 200, y = 200, w = 50, h = 50} end function love.update(dt) -- 碰撞检测 if love.math.intersectSegments(object1.x, object1.y, object1.x+object1.w, object1.y, object2.x, object2.y, object2.x+object2.w, object2.y) or love.math.intersectSegments(object1.x+object1.w, object1.y, object1.x+object1.w, object1.y+object1.h, object2.x, object2.y, object2.x, object2.y+object2.h) or love.math.intersectSegments(object1.x, object1.y+object1.h, object1.x+object1.w, object1.y+object1.h, object2.x, object2.y+object2.h, object2.x+object2.w, object2.y+object2.h) or love.math.intersectSegments(object1.x, object1.y, object1.x, object1.y+object1.h, object2.x+object2.w, object2.y, object2.x+object2.w, object2.y+object2.h) then -- 碰撞处理 end end function love.draw() love.graphics.rectangle("line", object1.x, object1.y, object1.w, object1.h) love.graphics.rectangle("line", object2.x, object2.y, object2.w, object2.h) end ``` 上述代码仅提供了一些基本的碰撞检测方法,具体的实现方式还需要根据项目需求进行调整和完善。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值