基于分离轴定理的碰撞检测

分离轴定理在碰撞检测中的应用

http://bbs.9ria.com/thread-143677-1-1.html


1.png
教程详情
难度:中级
使用平台: AS3,Flash
预计完成时间:一小时


源码下载.png 

分离轴定理在检测两个简单的多边形之间的碰撞或多边形与圆形之间的碰撞时经常用到。与其他所有算法一样,分离轴定理也并非所向披靡的完美存在。本教程中,我们就深入数学角度来研究本定理,同时也向读者展示了游戏开发中如何应用此定理的示例代码和demo。

:虽然本教程demo和源码使用的是Flash和AS3,但同理也可以将这个思想移植到其他游戏开发环境中。

定理的表述
分离轴定理(简称SAT)提出,若你能找到一条直线,使得两个多边形分离,那么这两个多边形之间就没有发生碰撞。就这么简单。
2.jpg 
在上图中,明显第二排的图形是存在碰撞的。无论你怎么尝试在两图形之间塞一条线进去,结果总是失败的。而第一行是另一种情况。你能够轻松地画一条直线(或是更多线)使得这些图形分离:
3.jpg 
好的,孩纸们现在应该已经都掌握了这个要点了。这个关键点就是,若你找到这样一条直线,那就图形之间就必然分离。接下来,我们就探讨该如何检测是否存在这样的直线。

在任意轴向上投影
4.jpg 
假设现在要进行碰检的两个多边形为四边形:位于左侧的box1和右侧的box2。显而易见,这两个四边形在水平方向上是分离的。用代码检测的方法,是先计算得到两四边形之间的水平距离,然后减去box1和box2各自的半宽:
  1. //Pseudo code to evaluate the separation of box1 and box2
  2. var length:Number = box2.x - box1.x;
  3. var half_width_box1:Number = box1.width*0.5;
  4. var half_width_box2:Number = box2.width*0.5;

  5. var gap_between_boxes:Number = length - half_width_box1 - half_width_box2;
  6. if(gap_between_boxes > 0) trace("It's a big gap between boxes")
  7. else if(gap_between_boxes == 0) trace("Boxes are touching each other")
  8. else if(gap_between_boxes < 0) trace("Boxes are penetrating each other")
复制代码

当四边形朝向改变,情况会是怎么样呢?
5.jpg 
虽然它们之间的距离保持不变,但我们不得不想另一种方法来计算图形中心之间的距离长以及和半宽值-此时是沿着P轴的。于是矢量代数就闪亮登场了。我们将向量A和B沿P投影,以得到半宽。

那接下来让我们小小复习下代数知识。

矢量代数复习
首先,复习下两个向量A和B之间的点积的概念:
6.jpg 
点积的定义可以由向量的两个分量实现:
7.png 
从另外一个角度理解点积,涉及向量大小以及夹角:
8.png 
现在,我们要求向量A在P上的投影。
9.jpg 
参考上图,我们求得投影值为 10.png (theta为A和P之间的夹角),虽然计算夹角值进而得到投影也能达到目的,但这样太麻烦了,我们其实有更直接的计算方式:
11.png 
注, 12.png 其实就是P的单位向量。

现在,不必直接使用方程右侧的公式,取而代之使用左侧也可以得到相同的结果。

应用到场景
在继续前进之前,我们来明确下四边形的角点命名。这在后续代码将会用到:
13.jpg 
场景如下:
14.jpg 
现有两个四边形偏离水平轴向45°,我们必须按顺序计算下面的长度,以求得四边形之间的距离。

  • A在P轴上的投影
  • B在P轴上的投影
  • C在P轴上的投影

投影得到正值,但由于向量指向的方向相反,B到P的投影值是负的。AS3实现如下,第25行就涉及了这个问题:
  1. var dot10:Point = box1.getDot(0);
  2. var dot11:Point = box1.getDot(1);

  3. var dot20:Point = box2.getDot(0);
  4. var dot24:Point = box2.getDot(4);

  5. //Actual calculations
  6. var axis:Vector2d = new Vector2d(1, -1).unitVector;
  7. var C:Vector2d = new Vector2d(
  8.     dot20.x - dot10.x,
  9.     dot20.y - dot10.y
  10. )
  11. var A:Vector2d = new Vector2d(
  12.     dot11.x - dot10.x,
  13.     dot11.y - dot10.y
  14. )
  15. var B:Vector2d = new Vector2d(
  16.     dot24.x - dot20.x,
  17.     dot24.y - dot20.y
  18. )
  19. var projC:Number = C.dotProduct(axis)
  20. var projA:Number = A.dotProduct(axis);
  21. var projB:Number = B.dotProduct(axis);

  22. var gap:Number = projC - projA + projB; //projB is expected to be a negative value
  23. if (gap > 0) t.text = "There's a gap between both boxes"
  24. else if (gap > 0) t.text = "Boxes are touching each other"
  25. else t.text = "Penetration had happened."
复制代码

以下demo对应上述代码。单击拖拽四边形内部的红色圆点,观察交互反应。

完整源代码请在源码下载中查看 DemoSAT1.as。

缺陷
以上确实顺利实现,但存在不少问题:
首先,向量A和B是固定的。所以当你交换box2和box1的位置,碰撞检测就会失败。
15.jpg 
第二,由于只沿一根轴进行距离值的计算,所以像下面所示的情况将得不到正确碰检结果:
16.jpg 
尽管前面的demo是有缺陷的,但不妨碍我们从中学习投影概念。接下来,我们就要对其进行改进。

解决第一个缺陷
首先,我们需要得到四边形各个角点到P的投影最小值和最大值(从原点到四边形顶点的向量)到P。
17.jpg 
上图展示了当四边形朝向刚好吻合向量P的情况下,角点到P的投影最小值和最大值。
但如果box1和box2的朝向与P不一致呢?
18.jpg 
上图展示了四边形并非刚好沿着P方向的情况下对应的最小最大投影。在这种情况下,我们就得遍历四边形的各个角点,以作出正确选择。

现在,我们已经得到最小-最大投影,那么接下来就得对四边形之间是否发生碰撞进行判断。要如何实现呢?
19.jpg 
通过上图的观察,我们可以清楚地看到box1.max以及box2.min到轴P上投影的几何表示。

如你所见,当两个四边形之间存在间隔, box2.min-box1.max将大于零,换句话说, box2.min > box1.max。当四边形位置交换,则变成box1.min > box2.max,这就表示它们不存在重叠。

将这一结论转化为代码,我们就会得到:
  1. //SAT: Pseudocode to evaluate the separation of box1 and box2
  2. if(box2.min>box1.max || box1.min>box2.max){
  3.     trace("collision along axis P happened")
  4. }
  5. else{
  6.     trace("no collision along axis P")
  7. }
复制代码

初始化代码
接下来呢,我们深入代码细节。注意,以下AS3代码未经优化。尽管下列代码有点冗长,还是能清楚了解到数学原理的应用。

首先,需要提供向量:
  1. //preparing the vectors from origin to points
  2. //since origin is (0,0), we can conveniently take the coordinates 
  3. //to form vectors
  4. var axis:Vector2d = new Vector2d(1, -1).unitVector;
  5. var vecs_box1:Vector.<Vector2d> = new Vector.<Vector2d>;
  6. var vecs_box2:Vector.<Vector2d> = new Vector.<Vector2d>;

  7. for (var i:int = 0; i < 5; i++) {
  8.     var corner_box1:Point = box1.getDot(i)
  9.     var corner_box2:Point = box2.getDot(i)
  10.      
  11.     vecs_box1.push(new Vector2d(corner_box1.x, corner_box1.y));
  12.     vecs_box2.push(new Vector2d(corner_box2.x, corner_box2.y));
  13. }
复制代码

接下来,要得到box1投影的最大值和最小值。box2中也会有类似的方法:
  1. //setting min max for box1
  2. var min_proj_box1:Number = vecs_box1[1].dotProduct(axis); 
  3. var min_dot_box1:int = 1;
  4. var max_proj_box1:Number = vecs_box1[1].dotProduct(axis); 
  5. var max_dot_box1:int = 1;

  6. for (var j:int = 2; j < vecs_box1.length; j++) 
  7. {
  8.     var curr_proj1:Number = vecs_box1[j].dotProduct(axis)
  9.     //select the maximum projection on axis to corresponding box corners
  10.     if (min_proj_box1 > curr_proj1) {
  11.         min_proj_box1 = curr_proj1
  12.         min_dot_box1 = j
  13.     }
  14.     //select the minimum projection on axis to corresponding box corners
  15.     if (curr_proj1> max_proj_box1) {
  16.         max_proj_box1 = curr_proj1
  17.         max_dot_box1 = j
  18.     }
  19. }
复制代码

最后,对特定轴P上是否存在碰撞进行判定:
  1. var isSeparated:Boolean = max_proj_box2 < min_proj_box1 || max_proj_box1 < min_proj_box2
  2. if (isSeparated) t.text = "There's a gap between both boxes"
  3. else t.text = "No gap calculated."
复制代码

以下为上述实现的demo:

可以拖拽四边形内部的点来移动,R和T键控制旋转。红色圆点表示该四边形的最大角点,黄色代表最小的。当四边形有两条边与P平行时,你拖拽四边形时会发现角点在闪烁,这是因为这两个角点有相同的特征。

完整代码可在源码下载中的DemoSAT2.as查看。

优化
进一步优化加速,p的单位向量其实是不需要计算的。因此,涉及Math.sqrt()的开销巨大的勾股定理计算有相当大部分可以跳过:
20.png21.jpg 
推导如下(参考上图中变量的辅助标记):
  1. /*
  2. Let:
  3. P_unit be the unit vector for P,
  4. P_mag be P's magnitude,
  5. v1_mag be v1's magnitude,
  6. v2_mag be v2's magnitude,
  7. theta_1 be the angle between v1 and P,
  8. theta_2 be the angle between v2 and P,

  9. Then:
  10. box1.max < box2.min
  11. => v1.dotProduct(P_unit) < v2.dotProduct(P_unit)
  12. => v1_mag*cos(theta_1) < v2_mag*cos(theta_2)
  13. */
复制代码

现在,如果不等式两边同时乘以一个相同的数字A(A>0),不等号方向不变:
  1. /*
  2. So:
  3. A*v1_mag*cos(theta_1) < A*v2_mag*cos(theta_2)

  4. If A is P_mag, then:
  5. P_mag*v1_mag*cos(theta_1) < P_mag*v2_mag*cos(theta_2)
  6. ...which is equivalent to saying:
  7. v1.dotProduct(P) < v2.dotProduct(P)
  8. */
复制代码

因而,这个是单位向量与否实际上并没多大关系——结果并不影响。

若只是检查是否重叠,那么这方法无疑非常好用。但如果需要得到box1与box2之间确切的距离值(大多数游戏可能会有要求),那你还是得对P的单位向量进行计算。

解决第二个缺陷
一个轴向的问题解决了,但还没结束。我们仍需要解决其他轴——问题是,选择哪个轴向呢?
22.jpg 
对四边形的探究相当直观:我们需要在两个轴P和q上进行比较。为确认碰撞的确存在,所有轴向上的重叠检测必须都通过——若存在某个轴向上轴没有重叠,那么可以得出没有碰撞的结论。

那么四边形朝向不同的情况呢?

单击绿色按钮可以跳转到另一个页面。在P,Q,R和S轴之中,有一个轴显示四边形没有重叠,于是我们得出结论,四边形之间不存在碰撞。

但现在的问题是,我们该如何确定哪些轴向可以用来检查重叠?嗯,先让我们来观察下多边形的法线。
23.jpg 
一般情况下,如果有两个四边形,那我们就必须沿着八个轴向检查: box1和box2各自的n0, n1, n2和n3轴向。然而,以下同一对其实是在同一轴向上的:

  • box1的n0和n2
  • box1的n1和n3
  • box2的n0和n2
  • box2的n1和n3

因此不需要对所有8轴向都进行检查,只要对4个进行就行了。若box1和box2朝向一致,那我们又可以进一步减少到只检测两个轴。

那对于其他多边形呢?
24.jpg 
很不幸的是,对于上述的三角形以及五角形无法走这样的捷径,得对所有法线方向进行检查。

法线计算
每个面都有两条法线:
25.jpg 
上图显示了p的左右法线。注意,向量两个分量的置换以及符号的不同。
26.jpg 
我的实现约定了顺时针,所以我使用左法线。以下提取了SimpleSquare.as的重要部分来展示。
  1. public function getNorm():Vector.<Vector2d> {
  2.     var normals:Vector.<Vector2d> = new Vector.<Vector2d>
  3.     for (var i:int = 1; i < dots.length-1; i++) 
  4.     {
  5.         var currentNormal:Vector2d = new Vector2d(
  6.             dots[i + 1].x - dots[i].x, 
  7.             dots[i + 1].y - dots[i].y
  8.         ).normL //left normals
  9.         normals.push(currentNormal);
  10.     }
  11.     normals.push(
  12.         new Vector2d(
  13.             dots[1].x - dots[dots.length-1].x, 
  14.             dots[1].y - dots[dots.length-1].y
  15.         ).normL
  16.     )
  17.     return normals;
  18. }
复制代码


全新实现
  1. //results of P, Q
  2. var result_P1:Object = getMinMax(vecs_box1, normals_box1[1]);
  3. var result_P2:Object = getMinMax(vecs_box2, normals_box1[1]);
  4. var result_Q1:Object = getMinMax(vecs_box1, normals_box1[0]);
  5. var result_Q2:Object = getMinMax(vecs_box2, normals_box1[0]);

  6. //results of R, S
  7. var result_R1:Object = getMinMax(vecs_box1, normals_box2[1]);
  8. var result_R2:Object = getMinMax(vecs_box2, normals_box2[1]);
  9. var result_S1:Object = getMinMax(vecs_box1, normals_box2[0]);
  10. var result_S2:Object = getMinMax(vecs_box2, normals_box2[0]);

  11. var separate_P:Boolean = result_P1.max_proj < result_P2.min_proj || 
  12.                          result_P2.max_proj < result_P1.min_proj
  13. var separate_Q:Boolean = result_Q1.max_proj < result_Q2.min_proj || 
  14.                          result_Q2.max_proj < result_Q1.min_proj
  15. var separate_R:Boolean = result_R1.max_proj < result_R2.min_proj || 
  16.                          result_R2.max_proj < result_R1.min_proj
  17. var separate_S:Boolean = result_S1.max_proj < result_S2.min_proj || 
  18.                          result_S2.max_proj < result_S1.min_proj

  19. //var isSeparated:Boolean = separate_p || separate_Q || separate_R || separate_S
  20. if (isSeparated) t.text = "Separated boxes"
  21. else t.text = "Collided boxes."
复制代码

其实不对某些变量进行计算也可以得到正确结果。若separate_P,separate_Q, separate_R和separate_S之中任意值为true,那表明多边形之间是分离的,后面步骤都可以跳过。

每次计算结束立即进行布尔值检查,符合条件的话就可以节省大量检测开销。这样就需要对代码进行部分修改,相信孩纸们能够自己完成的(或查看DemoSAT3.as的注释块)。

以下为上述实现的demo。单击拖拽四边形内部的圆点,R和T键用于旋转四边形:

后记
本算法运行时,它会检查各个轴向上是否存在重叠。在这里我有两点需要指出:


  • SAT属于乐天派。若多边形在某个轴向上没有重叠,那么这个算法就可以退出并愉快地得出“没有碰撞”的结论。如果一直检测出碰撞,那SAT就不得不遍历全部全部轴向直至结束。因此,碰撞检测不通过的情况越多,算法的实际性能越差。
  • SAT使用到多边形每条边上的法线。因此,多边形越是复杂,SAT开销越大。


六角形-三角形的碰撞检测
以下为另一个检测六角形和三角形之间是否存在碰撞的示例代码片段:
  1. private function refresh():void {
  2. //prepare the normals
  3. var normals_hex:Vector.<Vector2d> = hex.getNorm();
  4. var normals_tri:Vector.<Vector2d> = tri.getNorm();

  5. var vecs_hex:Vector.<Vector2d> = prepareVector(hex);
  6. var vecs_tri:Vector.<Vector2d> = prepareVector(tri);
  7. var isSeparated:Boolean = false;

  8. //use hexagon's normals to evaluate
  9. for (var i:int = 0; i < normals_hex.length; i++) 
  10. {
  11.     var result_box1:Object = getMinMax(vecs_hex, normals_hex[i]);
  12.     var result_box2:Object = getMinMax(vecs_tri, normals_hex[i]);
  13.      
  14.     isSeparated = result_box1.max_proj < result_box2.min_proj || result_box2.max_proj < result_box1.min_proj
  15.     if (isSeparated) break;
  16. }
  17. //use triangle's normals to evaluate
  18. if (!isSeparated) {
  19.     for (var j:int = 1; j < normals_tri.length; j++) 
  20.     {
  21.         var result_P1:Object = getMinMax(vecs_hex, normals_tri[j]);
  22.         var result_P2:Object = getMinMax(vecs_tri, normals_tri[j]);
  23.          
  24.         isSeparated = result_P1.max_proj < result_P2.min_proj || result_P2.max_proj < result_P1.min_proj
  25.         if (isSeparated) break;
  26.     }
  27. }

  28. if (isSeparated) t.text = "Separated boxes"
  29. else t.text = "Collided boxes."
  30. }
复制代码

完整代码可在源码下载中的DemoSAT4.as查看。

以下为demo。交互操作与之前的demo一样:拖拽中间的圆点,并使用R和T进行旋转。


四边形-圆形的碰撞检测
27.jpg 
与圆形之间的碰检相对简单。因为它的投影在所有方向上均相同(就只是圆的半径而已),可以就这么实现:
  1. private function refresh():void {
  2.     //prepare the vectors
  3.     var v:Vector2d;
  4.     var current_box_corner:Point;
  5.     var center_box:Point = box1.getDot(0);
  6.      
  7.     var max:Number = Number.NEGATIVE_INFINITY;
  8.     var box2circle:Vector2d = new Vector2d(c.x - center_box.x, c.y - center_box.y)
  9.     var box2circle_normalised:Vector2d = box2circle.unitVector
  10.      
  11.     //get the maximum
  12.     for (var i:int = 1; i < 5; i++) 
  13.     {
  14.         current_box_corner = box1.getDot(i)
  15.         v = new Vector2d(
  16.             current_box_corner.x - center_box.x , 
  17.             current_box_corner.y - center_box.y);
  18.         var current_proj:Number = v.dotProduct(box2circle_normalised)
  19.          
  20.         if (max < current_proj) max = current_proj;
  21.     }
  22.     if (box2circle.magnitude - max - c.radius > 0) t.text = "No Collision"
  23.     else t.text = "Collision"
  24. }
复制代码


完整源码请查看DemoSAT5.as。随便拖拽圆形或四边形来查看其碰撞。

小结
嗯,就是这样。感谢阅读,欢迎留言反馈问题。下个教程再见!

原文链接:http://gamedev.tutsplus.com/tutorials/implementation/collision-detection-with-the-separating-axis-theorem/


<think>首先,用户的问题是关于分离轴定理(SAT)在2D和3D碰撞检测中的原理和实现方法。我需要基于提供的引用来回答,确保内容真实可靠。系统指令:-所有行内数学表达式使用$...$格式。-独立公式使用$$...$$格式并单独成段。-LaTeX语法正确。-使用中文回答。-生成相关问题。-在回答中引用的段落末尾自然地添加引用标识,如[^1]。用户可见层要求:-回答结构清晰,帮助用户逐步解决问题。-保证回答真实可靠。参考的引用:-[^1]:SAT核心原理:如果两个凸形状在任何分离轴上投影不重叠,则不会碰撞。-[^2]:2DSAT方法:计算所有可能的分离轴(多边形边的法向量),检查投影是否重叠。-[^3]:3DSAT:类似原理,扩展到三维。-[^4]:实现细节:投影多边形的函数。-[^5]:2D实现:枚举所有边的法向量作为分离轴。我的回答结构:1.**介绍分离轴定理(SAT)的基本原理**:解释核心思想。2.**详细步骤**:如何实现SAT在2D和3D中。-2D实现:包括如何选择轴、投影多边形、检查重叠。-3D实现:扩展到三维,考虑面法向量等。3.**代码示例**:基于引用[4],提供简单的代码片段。4.**优缺点和应用**:讨论SAT的优缺点和常见应用场景。5.**生成相关问题**:在结尾添加相关问题。确保使用中文,数学表达式正确格式化。关键点从引用中提取:-SAT核心:如果存在一条轴,使两个凸多边形的投影不重叠,则它们不相交。-在2D中,分离轴是多边形边的法向量。-在3D中,分离轴是多边形面的法向量。-实现:投影多边形到轴上,检查投影区间是否重叠。逐步回答:1.**原理介绍**:用简单语言解释SAT。2.**2D实现步骤**:-步骤1:计算两个多边形的所有边的法向量。-步骤2:对于每个轴,投影两个多边形的所有顶点到该轴上。-步骤3:检查投影区间是否重叠;如果任何轴上不重叠,则无碰撞。3.**3D实现**:类似,但轴是面法向量,投影到线。4.**代码示例**:提供Python或伪代码示例,基于引用[4]。5.**注意事项**:SAT只适用于凸多边形;凹多边形需要分解。生成相关问题:-例如:如何处理凹多边形?SAT的性能如何?优化方法?</think>### 分离轴定理(SAT)碰撞检测算法原理与实现方法 分离轴定理(Separating Axis Theorem,SAT)是一种高效的碰撞检测算法,适用于**2D和3D凸多边形**。其核心思想基于几何投影:如果两个凸形状在任何一条可能的分离轴上投影区间不重叠,则它们不相交(无碰撞);反之,如果所有轴上投影均重叠,则发生碰撞[^1][^3]。下面我将逐步解释原理,并提供实现方法(包括2D和3D)。 #### 1. **算法原理** - **核心概念**:分离轴是一条直线(2D)或平面法线(3D)。算法检查两个多边形在每条轴上的投影是否重叠。 - 在2D中,分离轴是所有边的法向量(即垂直于边的向量)[^2][^5]。 - 在3D中,分离轴是所有面的法向量(垂直于面的向量)[^3]。 - **数学基础**: - 投影计算:将多边形的每个顶点投影到分离轴上,得到一个区间 $[\text{min}, \text{max}]$。投影公式使用点积: $$ \text{投影值} = \mathbf{v} \cdot \mathbf{a} $$ 其中 $\mathbf{v}$ 是顶点坐标向量,$\mathbf{a}$ 是分离轴单位向量。 - 重叠判断:对于两个投影区间 $[min_1, max_1]$ 和 $[min_2, max_2]$,如果不重叠(即 $max_1 < min_2$ 或 $max_2 < min_1$),则存在分离轴,无碰撞[^1][^4]。 #### 2. **2D实现方法** 适用于二维凸多边形(如矩形、三角形)。步骤清晰: 1. **计算所有分离轴**:遍历两个多边形的每条边,计算其法向量(单位化)。例如,边向量为 $(dx, dy)$,则法向量为 $(-dy, dx)$ 或 $(dy, -dx)$[^2][^5]。 2. **投影多边形**:对每个分离轴,计算两个多边形的投影区间(最小值和最大值)。 - 伪代码示例(基于引用[4]): ```python def project_polygon(axis, polygon): min_proj = max_proj = dot(axis, polygon.vertices[0]) # 点积计算 for vertex in polygon.vertices[1:]: proj = dot(axis, vertex) if proj < min_proj: min_proj = proj if proj > max_proj: max_proj = proj return (min_proj, max_proj) ``` 3. **检查重叠**:对每个轴,比较两个投影区间: - 如果存在轴使得 $max_1 < min_2$ 或 $max_2 < min_1$,则无碰撞,提前返回。 - 如果所有轴均重叠,则碰撞发生。 4. **优化**:在2D中,轴的数量等于两个多边形的边数之和。例如,两个矩形共8条边,需检查8条轴[^5]。 #### 3. **3D实现方法** 扩展到三维凸多面体(如立方体、四面体)。原理类似,但轴和投影更复杂: 1. **计算分离轴**:包括: - 所有面的法向量(单位化)。 - 所有边的叉积组合(避免遗漏,如两个多面体的边叉积)。 - 例如,两个立方体需检查15条轴(9个面法向量 + 6个边叉积轴)[^3]。 2. **投影和重叠检查**: - 投影方法同2D,但顶点是三维点。 - 区间重叠判断逻辑不变。 3. **实现注意**:3D计算量更大,需优化轴遍历顺序(如优先检查主法向量)。 #### 4. **优缺点和应用场景** - **优点**:高效($O(n)$ 复杂度,$n$ 为轴数),精确适用于凸形状[^1][^3]。 - **缺点**:仅适用于凸多边形;凹形状需先分解为凸多边形。投影计算可能受浮点误差影响。 - **应用**:游戏引擎(Unity、Unreal)、物理仿真(如刚体碰撞检测)[^4][^5]。 #### 5. **示例代码(2D Python实现)** 基于引用[4]简化: ```python import numpy as np class Polygon: def __init__(self, vertices): self.vertices = vertices # 顶点列表,如 [[x1,y1], [x2,y2], ...] def dot(v1, v2): return v1[0]*v2[0] + v1[1]*v2[1] def project(axis, poly): min_proj = max_proj = dot(axis, poly.vertices[0]) for v in poly.vertices[1:]: proj = dot(axis, v) min_proj = min(min_proj, proj) max_proj = max(max_proj, proj) return min_proj, max_proj def sat_collision_2d(poly1, poly2): # 获取所有边的法向量 edges = [] for poly in [poly1, poly2]: for i in range(len(poly.vertices)): v1 = poly.vertices[i] v2 = poly.vertices[(i+1) % len(poly.vertices)] edge = [v2[0]-v1[0], v2[1]-v1[1]] normal = [-edge[1], edge[0]] # 法向量 norm = np.linalg.norm(normal) if norm > 0: normal = [normal[0]/norm, normal[1]/norm] # 单位化 edges.append(normal) # 检查所有轴 for axis in edges: min1, max1 = project(axis, poly1) min2, max2 = project(axis, poly2) if max1 < min2 or max2 < min1: return False # 无碰撞 return True # 碰撞 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值