用于二维碰撞检测的四叉树

Posted

技术标签:

【中文标题】用于二维碰撞检测的四叉树【英文标题】:Quadtree for 2D collision detection 【发布时间】:2011-06-26 07:05:24 【问题描述】:

我正在尝试使用四叉树进行 2D 碰撞检测,但我对如何实现它有点困惑。首先,我有一个四叉树,它包含四个子树(一个代表每个象限),以及一组不适合单个子树的对象。

在检查树中的对象是否有碰撞时,我会做这样的事情(感谢QuadTree for 2D collision detection):

    检查对象是否与当前节点中的任何对象发生冲突。 对于空间与对象重叠的任何子树,递归。

要查找四叉树中的所有碰撞:

    检查当前节点中的每个对象与当前节点中的其他对象。 根据每个子树检查当前节点中的每个对象。

插入四叉树:

    如果对象适合多个子树,则将其添加到当前节点,然后返回。 否则,递归到包含它的任何子树。

更新四叉树:

    递归到每个子树。 如果当前节点中的任何元素不再完全适合当前树,请将其移至父节点。 如果当前节点中的任何元素适合子树,则将其插入子树。

这样好吗?可以改进吗?

【问题讨论】:

按照您描述的方式实现它,我已经这样做了,Dave O. 也这样做了,这更容易编码,而且速度更快。管理更多列表以跟踪您的所有叶子会增加可避免的开销。这是我的一个版本的源代码(Java):Steerio 【参考方案1】:

您的四叉树结构不是最优的。每个节点存储 4 个子树是对的,但实际对象应该只存储在叶子内部,而不是内部节点。因此,保存实际对象的集合需要移动到叶子。

让我们看看操作的实现

    在四叉树中插入一个对象: 检查对象是否与当前节点相交。如果是,递归。如果您已达到叶级别,请将对象插入到集合中。 从四叉树中删除一个对象: 执行与插入对象完全相同的步骤,但当您达到叶级别时,将其从集合中删除。 测试一个对象是否与四叉树内的任何对象相交: 执行与插入对象完全相同的步骤,但是当您达到叶级别时,请检查是否与集合中的所有对象发生冲突。 测试四叉树内所有对象之间的所有碰撞: 对四叉树中的每个对象执行单个对象碰撞测试。 更新四叉树: 从四叉树中删除所有位置已修改的对象,然后重新插入。

这有几个优点

通过仅将对象存储在叶子中,处理四叉树上的操作非常容易(特殊情况较少) 四叉树可以有不同深度的叶子,因此您可以根据四叉树覆盖的空间区域调整四叉树的密度。这种适应可以在运行时发生,从而使对象/节点比率保持最佳。

只有缺点

对象可以属于四叉树内的多个集合。您将需要在四叉树之外有一个额外的线性集合来枚举每个没有重复的对象。

【讨论】:

这是我担心的额外缺点。这不会导致添加额外的代码(例如四叉树之外的线性集合)以确保 A 只与 B 发生一次碰撞,即使 B 可能在多个子树中? @RobotGymnast 碰撞不是问题,因为您只返回真/假,并且如果一个对象属于多个集合,那么它们之间仍然是相同的。枚举是。您不能使用四叉树遍历所有对象,因为您会多次访问其中一些对象。 这可能很幼稚,但是给对象添加一个touched()类型的函数呢?这样,在遍历一个被检查的对象时,您可以设置一个已触摸的标志,因此如果它重新出现则忽略它?我知道这可能不是一个完全干净甚至优雅的方法,但它看起来很简单,而且我已经在我的四叉树实现中使用它来取得很好的效果。 我刚刚发现的另一个缺点是,如果你有四个大对象,比如说,覆盖整棵树,它会细分,直到你达到深度限制。添加第五个,如果没有一些复杂的边缘案例处理,它会崩溃。 还有一个问题是,对于许多对象(线、圆、矩形,甚至多边形)来说,计算边界矩形比计算交叉点要便宜得多且简单。【参考方案2】:

四叉树并不总是碰撞检测的最佳数据结构。四叉树的开销可能是无限的(如果您不限制树的深度),并且在最坏的情况下根本不给予任何加速。相反,您可能需要考虑使用稀疏网格,它比四叉树提供更好的性能,而无需遍历多个树级别的额外开销。

还有其他完全不同的方法可能会更好。例如,您可以尝试实现 Zomorodian 和 Edelsbrunner 的算法,就像我在以下模块中所做的那样:

https://github.com/mikolalysenko/box-intersect

这里还有一些我写的文章更详细地讨论了这些问题:

http://0fps.net/2015/01/07/collision-detection-part-1/ http://0fps.net/2015/01/18/collision-detection-part-2/ http://0fps.net/2015/01/23/collision-detection-part-3-benchmarks/

特别是,如果您查看上一节中的基准,您会发现在所有调查的库中,与 R-Tree、网格或分段树等其他碰撞检测方法相比,四叉树的性能往往很差。

【讨论】:

这些博文非常棒。我发现了一些关于这个主题的最佳信息【参考方案3】:

我不确定它的 cpu 效率如何,但它似乎在我的 eclipse 中的核心二重奏上运行良好,仍然以超过 2400 fps 的速度运行,哈哈。

基本上,我向可碰撞对象添加了一个列表,以存储对与该对象相关联的四叉树节点对象的引用(通过插入四叉树)。我还向每个四叉树节点添加了一个列表,该列表存储对该节点范围内的任何对象的引用。所以每个节点只会出现每个对象一次。每个节点还存储对其父节点的引用,以便导航到附近的节点,如果我想在初始节点之后检查它们中的任何一个以获得进一步的碰撞准确性。

在一个单元格中获取对所有其他对象的引用非常容易:

list temp_checklist = object.cells[cell_index].objects
//('objects' being some sort of array or list of object references as described above)

希望对某人有所帮助;)

【讨论】:

以上是关于用于二维碰撞检测的四叉树的主要内容,如果未能解决你的问题,请参考以下文章

四叉树与碰撞检测 !Cocos Creator !

四叉树优化碰撞检测

[译]2D空间中使用四叉树Quadtree进行碰撞检测优化

深入理解Unity的碰撞检测机制

四叉树写着玩

何时使用二进制空间分区、四叉树、八叉树?