宽相碰撞检测方法?

时间:2009-10-23 23:29:35

标签: algorithm physics collision-detection broad-phase

我正在构建一个2D物理引擎,我想添加广角碰撞检测,但我只知道2或3种类型:

  • 检查所有内容(O(n ^ 2)复杂度)
  • 扫描和修剪(排序和扫描)
  • 二进制空间分区(不知道如何做)

但肯定还有更多选择吗?这些是什么?并且可以提供每个的基本描述或链接到描述吗?

我见过this但是我要求提供一系列可用的算法,而不是最适合我需要的算法。

在这种情况下,“广泛相位碰撞检测”是物理引擎用来确定模拟中哪些物体足够接近以保证进一步调查并可能解决冲突的方法。

10 个答案:

答案 0 :(得分:20)

最好的方法取决于具体用途,但最重要的是你要细分你的世界空间,以便(a)每个身体只有一个细分,(b)每个细分都足够大,以至于身体在特定细分只能与同一细分或相邻细分中的实体发生碰撞,并且(c)特定细分中的实体数量尽可能小。

你如何做到这一点取决于你拥有多少身体,他们如何移动,你的性能要求是什么,以及你想在引擎上花多少时间。如果你在谈论在一个很大的开放空间中移动的物体,最简单的技术就是将世界划分为一个网格,每个单元格大于最大的对象,并跟踪每个单元格中的对象列表。如果你正在构建一个经典街机游戏的规模,这个解决方案可能就足够了。

如果你正在处理在更大的开放世界中移动的物体,一个简单的网格将很快变得势不可挡,你可能会想要某种类似四叉树的基于树的结构,正如Arriu所暗示的那样。

如果你在谈论在有界空间内移动身体而不是开放空间,那么你可以考虑BSP tree;树将世界划分为“你可以走进的空间”和“墙壁”,并将树体剪入树中,以确定它是否处于合法位置。根据世界几何形状,您还可以使用BSP进行世界各体之间碰撞的广泛检测。

在有界空间中移动的物体的另一个选择是门户引擎;如果你的世界可以由凸多边形区域组成,其中多边形的每一边都是实心墙或另一个凹空间的“门户”,你可以很容易地确定一个体是否在一个带有多边形点测试的区域内。通过仅查看同一区域或连接区域中的实体来简化碰撞检测。

答案 1 :(得分:9)

QuadTrees或BSPTrees的替代方案是SphereTrees(2D中的CircleTrees,实现方式或多或少相同)。 SphereTrees的优势在于它们可以很好地处理大量动态对象。如果你的对象不断移动,BSPTrees和QuadTrees的更新速度要比Sphere / Circle Tree慢得多。

如果你有很好的静态动态对象组合,一个相当不错的解决方案是使用QuadTree / BSPTree作为静态,使用Sphere / Cicle树作为动态对象。请记住,对于任何给定的对象,您需要针对两个树进行检查。

答案 2 :(得分:6)

我建议quadtree分区。它非常简单,效果非常好。以下是蛮力碰撞检测与四叉树碰撞检测的Flash demo。 (您可以告诉它显示四叉树结构。)您是否注意到四叉树碰撞检测在该演示中仅占蛮力的3%?

另外,如果您认真对待引擎,那么我强烈建议您选择real-time collision detection。它并不昂贵,它是一本非常好的书,涵盖了你想知道的一切。 (包括基于GPU的碰撞检测。)

答案 3 :(得分:2)

所有加速算法都依赖于使用廉价测试来快速排除对象(或对象组),从而减少您必须执行的昂贵测试的数量。我在类别中查看算法,每个算法都有很多变化。

  1. 空间分区。占用空间并廉价地排除不同地区的候选人。例如,BSP,网格,八叉树等

  2. 对象分区。与#1类似,但聚类的重点是对象本身而不是它们所在的空间。例如,绑定卷层次结构。

  3. 排序和扫描方法。按顺序放置对象,排除不相邻的对象。

  4. 1和2通常是分层的,根据需要递归到每个分区。使用3,您可以选择沿不同维度进行迭代。

    权衡取决于场景几何。对象是集群还是均匀或稀疏分布?它们的尺寸大小相同还是尺寸差异很大?场景动态吗?你能承受很多预处理时间吗?

    “便宜”的测试实际上是一种非常便宜的,价格昂贵的。选择最佳算法意味着最小化廉价测试成本与昂贵测试数量减少的比率。除了算法问题之外,你还要进行调优,比如担心缓存局部性。

答案 4 :(得分:1)

替代方案是普通网格,例如20x20或100x100(取决于您的世界和内存大小)。实现比递归结构更简单,例如quad / bsp-trees(或者球形树)。

在这种情况下,跨越单元格边框的对象稍微简单一些,并且不会像bsp / quad / oct-tree的天真实现那样退化。

使用它(或其他技术),您应该能够快速剔除许多对并获得一组需要进一步调查的潜在碰撞。

答案 5 :(得分:1)

我想出了一个不依赖于网格大小的解决方案,可能是O(nlogn)(这是没有碰撞时的最佳选择),尽管O(n n logn)(当一切都发生碰撞时)。

我也实施并测试了它,这里是source的链接。但我没有把它与蛮力解决方案进行比较。

它是如何工作的描述:     (我正在寻找矩形的碰撞)

    x轴上的
  • 我根据它们的右边缘(O(nlogn))对矩形进行排序

  • 对于排序列表中的每个矩形,我采用左边缘并进行二分搜索,直到找到左边缘左边最右边的边缘,并在possible_Collision_On_X_Axis集合中将这些矩形插入这些索引之间(这些是n个矩形,logn用于二进制搜索,n在每个插入的O​​(log n)处插入int集合

  • y轴上的
  • 我也是这样做的

  • 在每个集合中我现在可能在x(在一个集合中)和在y(在另一个集合中)上发生碰撞,我与这些集合相交,现在我在x轴和y轴上都有碰撞(这意味着我采用共同的元素)(O(n))

抱歉描述不佳,希望您从源头上了解得更好,这里也举例说明:image

答案 6 :(得分:1)

您可能希望查看Scott在Chipmunk中使用空格散列执行的操作。来源免费提供。如果不是碰撞,我认为他使用类似的技术Box-2D,绝对是接触持久性。

答案 7 :(得分:0)

如果对象在其中移动的空间有界,则可以使用网格细分对象。将每个对象放入对象覆盖的每个网格单元格中(完全或部分)。要检查对象A是否与任何其他对象发生碰撞,请确定对象A覆盖的网格单元格,然后获取这些单元格中的唯一对象列表,最后针对每个唯一对象测试对象A.如果大多数对象通常包含在单个网格单元格中,则此方法效果最佳。

如果您的空间不受限制,那么您将需要实现某种动态网格,这些网格可以在四个方向中的每个方向上按需增长(2D)。

这种方法优于更自适应的算法(即BSP,四叉树,Circletree)的优点是可以在O(1)时间(即恒定时间)而不是O(log N)时间(即对数时间)访问这些单元。 )。然而,后面这些算法能够适应对象密度的大变化。当物体密度在整个空间内相当恒定时,网格方法效果最佳。

答案 8 :(得分:0)

我想从Ian Millington Game Physics Engine Development推荐游戏物理的介绍性参考。它有一个关于宽相位碰撞检测和样本代码的重要部分。

答案 9 :(得分:0)

我在一个较大的项目中使用了四叉树,这对于不会移动太多的游戏对象(减少删除和重新插入)很有用。同样的原则适用于八叉树。

基本的想法是,您创建一个递归树结构,它存储与树根相同类型的4个(对于quad)或8个(oct)子对象。树中的每个节点表示笛卡尔空间的一部分,例如,-100 - >每个适用轴上+100。每个子代表相同的空间,但细分为一半(该示例的直接子代表示例如X轴上的-100> 0,Y轴上的-100> 0)。

想象一个具有一组给定尺寸的正方形或平面。现在在每个轴上绘制一条穿过中心的线,将该平面划分为4个较小的平面。现在再拿一个并再次执行它,直到达到平面段大小大致与游戏对象大小相同的程度。现在你有了你的树。只有占据同一平面的物体才有可能发生碰撞。当您确定可以碰撞的对象时,您会从它们生成可能的碰撞对。在这个阶段,宽相完成,你可以进行窄相位碰撞检测,这是你更精确,更昂贵的计算。

Broadphase的目的,是使用廉价的计算来产生可能的碰撞,并剔除不能发生的联系,从而减少你的窄阶段算法必须执行的工作,反过来,使你的整个碰撞检测算法更有效率。

所以在你开始尝试实现这样的算法之前,就像你自己一样:

我的游戏中有多少个物体? 如果有很多,我可能需要一个广泛的阶段。如果没有,那么Nnarrowphase就足够了。 我还在处理许多移动物体吗?

树状结构通常会因移动物体而减慢,因为它们可以随着时间的推移改变它们在树中的位置,只需移动即可。这要求每个帧(可能)移除和重新插入对象,这不是理想的。

如果是这种情况,你最好使用Sweep和Prune,它可以保持你世界中形状范围的最小/最大堆。对象不需要重新插入,但需要在每个帧中使用堆,认为这通常比树宽更快,遍历删除和重新插入。

根据您的编码经验,编码可能会更复杂。就个人而言,我发现树木更直观,但效率更低,更容易出错,因为它们引发了其他问题,例如,如果你有一个直接位于轴顶部的物体或中心,该怎么办根节点。这可以通过使用具有一些空间重叠的松散树来解决,这样在发生这种情况时,一个子节点总是优先于其他子节点。