33

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

当在树中检查对象的碰撞时,我会做这样的事情(感谢QuadTree 的 2D 碰撞检测):

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

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

  1. 检查当前节点中的每个对象与当前节点中的其他对象。
  2. 针对每个子树检查当前节点中的每个对象。

插入四叉树:

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

更新四叉树:

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

这可以吗?可以改进吗?

4

3 回答 3

38

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

让我们看一下操作的实现:

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

这有几个优点

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

唯一的缺点

  • 对象可以属于四叉树内的多个集合。您将需要在四叉树之外有一个额外的线性集合来枚举每个对象而不会重复。
于 2011-02-13T03:05:32.997 回答
13

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

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

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

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

于 2015-06-15T20:31:22.097 回答
0

我不确定它的 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)

希望对某人有所帮助;)

于 2014-05-08T08:21:40.860 回答