【问题标题】:Broad-phase collision detection methods?宽相碰撞检测方法?
【发布时间】:2009-10-23 23:29:34
【问题描述】:

我正在构建一个 2D 物理引擎,我想添加宽相位碰撞检测,虽然我只知道 2 或 3 种类型:

  • 对照其他所有内容检查所有内容(O(n^2) 复杂度)
  • Sweep and Prune(排序和扫描)
  • 关于二进制空间分区的一些信息(不确定如何执行此操作)

但肯定还有更多选择,对吧?这些是什么?是否可以提供每个的基本描述或描述的链接?

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

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

【问题讨论】:

  • 如果您用更笼统的术语表达您的问题,您可能会得到更好的答复。我对碰撞检测、复杂性和其他一些事情知之甚少,但“广泛相碰撞检测”似乎是特定领域的术语;我敢打赌这是一个简单概念的代码,但我不想花时间研究。
  • 感谢大家的所有答案,尽管它们都很好,但它们都涵盖了一种类型的碰撞检测。如果有人可以继续列出此处列出的所有类型,我将很高兴接受,否则我将不得不自己制作......

标签: algorithm physics collision-detection broad-phase


【解决方案1】:

最佳方法取决于具体用途,但底线是您希望细分您的世界空间,以便 (a) 每个身体都在一个细分中,(b) 每个细分都足够大,一个身体在特定细分只能与同一细分或相邻细分中的物体发生碰撞,并且(c)特定细分中的物体数量尽可能少。

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

如果您要处理在更大的开放世界中移动的物体,那么简单的网格很快就会变得不堪重负,并且您可能需要某种基于树的结构,如 Arriu 建议的四叉树。

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

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

【讨论】:

    【解决方案2】:

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

    如果您有静态动态对象的良好组合,一个相当好的解决方案是对静态对象使用 QuadTree/BSPTree,对动态对象使用 Sphere/Cicle Tree。请记住,对于任何给定的对象,您都需要对照两棵树检查它。

    【讨论】:

      【解决方案3】:

      我推荐quadtree 分区。这很简单,而且效果很好。这是暴力碰撞检测与四叉树碰撞检测的 Flash demo。 (您可以告诉它显示四叉树结构。)您是否注意到该演示中四叉树碰撞检测仅占蛮力的 3%?

      另外,如果您对自己的引擎很认真,那么我强烈建议您选择real-time collision detection。它并不昂贵,而且是一本非常棒的书,涵盖了您想知道的所有内容。 (包括基于 GPU 的碰撞检测。)

      【讨论】:

      • 好书推荐,我可能需要看看。
      • 链接已失效。 :(
      【解决方案4】:

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

      1. 空间分区。瓜分空间,廉价地排除不同地区的候选人。比如BSP、网格、八叉树等。

      2. 对象分区。与 #1 类似,但聚类更关注对象本身而不是对象所在的空间。例如,包围体层次结构。

      3. 排序和扫描方法。将对象在空间上按顺序排列,并排除不相邻对象之间的碰撞。

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

      取舍很大程度上取决于场景几何。对象是聚集在一起还是均匀分布或稀疏分布?它们的大小都差不多还是大小有很大差异?场景是动态的吗?你能承受大量的预处理时间吗?

      “便宜”的测试实际上是从非常便宜到有点贵的范围内。选择最佳算法意味着最小化廉价测试的成本与减少昂贵测试数量的比率。除了算法问题之外,您还需要进行调优,比如担心缓存位置。

      【讨论】:

        【解决方案5】:

        另一种选择是普通网格,例如 20x20 或 100x100(取决于您的世界和内存大小)。该实现比四叉树/bsp 树(或球树)等递归结构更简单。

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

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

        【讨论】:

          【解决方案6】:

          我刚刚提出了一个不依赖于网格大小的解决方案,并且可能是 O(nlogn)(这是没有冲突时的最佳值),但最差的时间是 O(nn logn)(当一切发生冲突时)。

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

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

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

          • 对于排序列表中的每个矩形,我取左边缘并进行二进制搜索,直到在左边缘的左侧找到最右边的边缘并将这些矩形插入到可能n 个矩形,logn 用于二分查找,n 次插入 int 集合,每次插入 O(log n))

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

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

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

          【讨论】:

          • 我理解得很好,其实我也有类似的东西(Sweep And Prune 是正式名称)。一个提示;你在使用插入排序吗?它是可用于几乎排序列表的最佳算法(就像您在那里一样),sorting-algorithms.com/insertion-sort 提供了很好的解释/可视化
          • 谢谢你的名字,我认为这是一个不同的算法,在我的语言中类似地命名。该列表不一定几乎是排序的,因为矩形会及时移动(因此变得随机)所以我使用 std::sort
          • 但我们的想法是每次更新调用一次,这样您就可以依赖几乎已排序的列表并使用插入排序来获得最佳性能。
          【解决方案7】:

          您可能想看看 Scott 在 Chipmunk 中使用空间散列做了什么。源是免费的。我认为他使用了与Box-2D 类似的技术,如果不是为了碰撞,肯定是为了保持联系。

          【讨论】:

            【解决方案8】:

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

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

            想象一个具有给定尺寸的正方形或平面。现在在每个轴的中心画一条线,将该平面分成 4 个较小的平面。现在取其中一个,一次又一次地做,直到你到达一个平面段的大小大致是游戏对象大小的点。现在你有了你的树。只有占据同一平面的物体才有可能发生碰撞。当您确定哪些对象可以发生碰撞时,您会从它们生成可能的碰撞对。在这个阶段,broadphase 已经完成,您可以进入窄相碰撞检测,这是您进行更精确、更昂贵的计算的地方。

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

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

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

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

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

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

            【讨论】:

              【解决方案9】:

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

              如果您的空间没有界限,那么您将需要实现某种动态网格,该网格可以在四个方向(二维)中的每一个方向上按需增长。

              与更具自适应性的算法(即 BSP、Quadtree、Circletree)相比,这种方法的优势在于可以在 O(1) 时间(即恒定时间)而不是 O(log N) 时间(即对数时间)内访问单元)。然而,后一种算法能够使自己适应物体密度的巨大变化。当对象密度在整个空间中相当恒定时,网格方法效果最佳。

              【讨论】:

              • 我不确定“hashtable”是否完全算作一种特别相关的碰撞检测算法。
              • 这不是哈希表方法。任何碰撞检测算法都会细分空间,以减少所需的对象与对象比较的数量(如问题中所述)。这个问题的其他答案提到了空间的自适应分区。我的回答是指定期的、非自适应的空间划分。这在效率与对空间内对象不均匀分布的适应性之间进行了明显的权衡。
              【解决方案10】:

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2019-04-30
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多