基于四叉树与网格的碰撞检测


27

我正在制作一个4人合作R型游戏,并且即将实现碰撞检测代码。我已经阅读了许多有关如何处理碰撞检测的文章和内容,但是我很难弄清楚该如何处理。似乎四叉树是最常用的方法,但是在某些资源中,他们提到了基于网格的解决方案。对于以前的游戏中使用网格进行检测的情况,我对此感到满意,但是它实际上比四叉树好吗?我不确定哪个提供了最佳性能,并且我还运行了一个基准测试,两种解决方案之间没有太大区别。

这个比那个好吗 ?或更优雅?我真的不确定应该使用哪一个。

欢迎任何建议。谢谢。

Answers:


31

正确的答案在某种程度上取决于您正在设计的实际游戏,而选择一个游戏确实需要同时实现这两个功能并进行性能分析,以找出哪种游戏在您的特定游戏上更节省时间或空间。

网格检测似乎仅适用于检测运动对象与静态背景之间的碰撞。这样做的最大好处是,静态背景表示为连续的内存阵列,并且如果需要进行多次读取(因为实体覆盖网格中的多个单元格),则每次冲突查找都是具有良好局部性的O(1)。如果静态背景很大,则缺点是网格可能非常浪费空间。

相反,如果将静态背景表示为四叉树,则单个查找的开销会增加,但是由于背景的大块占用了少量空间,因此内存需求下降了,因此更多的背景可以放在缓存。即使在这种结构中进行查找所需的读取次数是其的10倍,即使它们全部在高速缓存中,它仍然比具有高速缓存未命中的单个查找快10倍。

如果我面临选择?我会选择网格实现,因为这样做很愚蠢,最好将时间花在其他更有趣的问题上。如果我发现我的游戏运行缓慢,我将进行一些分析,看看有什么可以使用的帮助。如果看起来游戏花费大量时间进行碰撞检测,那么我将尝试另一种实现,例如四叉树(首先用尽所有简单的修复方法),然后找出是否有帮助。

编辑:我不知道网格碰撞检测与检测多个移动实体的碰撞有何关系,但是,我将回答空间索引(Quadtree)如何通过迭代解决方案提高检测性能。天真的(通常非常完美)的解决方案看起来像这样:

foreach actor in actorList:
    foreach target in actorList:
        if (actor != target) and actor.boundingbox intersects target.boundingbox:
            actor.doCollision(target)

这显然具有O(n ^ 2)左右的性能,目前游戏中还活着的演员数量为n,其中包括子弹,宇宙飞船和外星人。它还可能包括小的静态障碍物。

只要此类项目的数量相当少,此方法就可以很好地发挥作用,但是当要检查的对象数量超过数百个时,它就会开始显得有些穷。10个对象仅进行100次碰撞检查,100个结果进行10,000次检查。1000结果产生一百万张支票。

空间索引(如四叉树)可以根据几何关系有效地枚举其收集的项目。这会将碰撞算法更改为以下形式:

foreach actor in actorList:
    foreach target in actorIndex.neighbors(actor.boundingbox):
       if (actor != target) and actor.boundingbox intersects target.boundingbox:
            actor.doCollision(target)

这样的效率(假设实体的均匀分布):通常为O(n ^ 1.5 log(n)),由于索引要遍历约log(n)个比较,因此要比较约有sqrt(n)个邻居,并且有n位演员需要检查。但是实际上,邻居的数量总是非常有限的,因为如果确实发生了碰撞,大多数情况下,一个对象会被删除或从碰撞中移开。因此,您只得到O(n log(n))。对于10个实体,您进行(大约)10个比较,对于100个实体,您进行200个,对于1000个实体,您进行3000个。

一个真正聪明的索引甚至可以将邻居搜索与批量迭代结合起来,并对每个相交的实体执行回调。由于索引被扫描一次而不是被查询n次,因此这将带来大约O(n)的性能。


我不确定您说“静态背景”时所指的是什么。我要处理的基本上是2D射击游戏,因此它是与太空飞船,外星人,子弹和墙壁的碰撞检测。
dotminic 2010年

2
您刚刚获得了我的私人“好答案”徽章!
Felixyz 2010年

这听起来可能很愚蠢,但是实际上我该如何使用四叉树选择一个对象应该针对哪些其他对象进行碰撞测试?我不确定如何做到这一点。这就提出了第二个问题。假设我在节点中有一个对象,该对象不与另一个节点相邻,但该对象足够大,可以跨越几个节点,我如何检查实际的冲突,因为我猜树可能认为它不是足够近以与“遥远”节点中的对象碰撞?不完全适合节点的对象应该保留在父节点中吗?
dotminic 2010年

2
对于重叠的边界框搜索,四叉树的本质是次优的。最好的选择通常是R-Tree。对于四叉树,如果大多数对象都大致呈点状,则可以,将对象保留在内部节点上,并对模糊邻居搜索执行精确的碰撞测试是合理的。如果索引中的大多数对象很大并且重叠而没有碰撞,则四叉树可能是一个糟糕的选择。如果您对此有更多技术问题,则应考虑将其带到stackoverflow.com
SingleNegationElimination 2010年

这一切都令人困惑!谢谢(你的)信息。
dotminic 2010年

3

很抱歉,要恢复古老的线程,但是在这些情况下,恕我直言,普通的老式旧网格使用得并不多。网格有很多优点,因为插入/取出单元格便宜。您不必费心释放单元格,因为网格的目标不是针对稀疏表示进行优化。我说过,通过仅用网格替换四叉树,减少了在传统代码库中选取大量元素的时间,从1200ms降低到20ms。公平地说,该四叉树的实现效果很差,每个叶节点为元素存储一个单独的动态数组。

我发现另一个非常有用的方法是,可以使用用于绘制形状的经典栅格化算法来对网格进行搜索。例如,您可以使用Bresenham线栅格化来搜索与线相交的元素,使用扫描线栅格化来查找与多边形相交的单元格,等等。由于我在图像处理方面做了大量工作,因此能够使用完全相同的图像真的很不错我用于检测像素与网格中移动物体的交点时用于绘制像素的优化代码。

就是说,要使网格高效,每个网格单元不需要超过32位。您应该能够在4兆字节内存储一百万个单元。每个网格单元都可以仅索引该单元中的第一个元素,然后该单元中的第一个元素可以索引该单元中的下一个元素。如果您要在每个单元格中存储某种成熟的容器,则在使用内存和快速分配内存方面会爆炸性增长。相反,您可以执行以下操作:

struct Node
{
    int32_t next;
    ...
};

struct Grid
{
     vector<int32_t> cells;
     vector<Node> nodes;
};

像这样:

在此处输入图片说明

好的,顺便说一句。坦率地说,我对网格有偏见和偏爱,但是它们的主要缺点是它们并不稀疏。

在给定坐标的情况下访问特定的网格单元是恒定时间的,不需要下降便宜的树,但是网格密集而不稀疏,因此最终可能需要检查比所需更多的单元。在您的数据分布非常稀疏的情况下,网格可能需要更多检查方法来找出相交的元素,例如线,实心多边形,矩形或边界圆。即使完全空缺,网格也必须存储该32位单元格,并且在执行形状相交查询时,必须检查那些空单元格是否与您的形状相交。

四叉树的主要优点自然是其存储稀疏数据并仅根据需要进行细分的能力。也就是说,很难很好地实现,尤其是当您在每个框架中都在移动的情况下。该树需要高效地细分和释放子节点,否则它会退化为密集的网格,浪费了开销来存储父级->子级链接。使用与我上面针对网格描述的技术非常相似的技术来实现高效的四叉树是非常可行的,但是通常会花费更多时间。而且,如果按照我在网格中所做的方式进行操作,那也不一定是最优的,因为这将导致保证四叉树节点的所有四个子级连续存储的能力下降。

同样,如果您有许多跨越整个场景的大型元素,则四叉树和网格都不会做得很好,但是在这种情况下,至少网格保持平坦并且不会细分为n级。四叉树应该将元素存储在分支中,而不仅仅是叶子来合理地处理这种情况,否则它会像疯了一样细分并且质量会迅速下降。如果要让四叉树处理最广泛的内容,则需要处理更多此类病理情况。例如,如果您有大量的重合元素,那么另一种可能真正导致四叉树跳闸的情况。那时,有些人只是为四叉树设置深度限制以防止其无限细分。网格的吸引力在于它做得不错,

稳定性和可预测性在游戏环境中也非常有用,因为有时在某些情况下,您可能并不需要最快的解决方案(如果在极少数情况下,偶尔会导致帧速率下降),而在某种情况下,解决方案应该是相当快的全速解决方案,但不会导致此类故障,并保持帧速率平稳且可预测。网格具有后一种品质。

综上所述,我真的认为这取决于程序员。对于网格,四叉树,八叉树,kd树与BVH之类的问题,我的投票是最有能力的开发人员,无论其使用哪种数据结构,其创造的解决方案都非常有效。在微级别上也有很多东西,例如多线程,SIMD,缓存友好的内存布局和访问模式。某些人可能会考虑这些微观因素,但不一定会产生微观影响。从一种解决方案到另一种解决方案,这些事情可能会造成100倍的差异。尽管如此,如果让我个人待几天,并被告知我需要实现一个数据结构以快速加速每帧移动的元素的碰撞检测,那么在短时间内实现网格的效果要比四分之一更好。 -树。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.