Answers:
使用一种常见的空间分区算法,例如四叉树,八叉树,BSP树,甚至是简单的网格系统。每个人在每种情况下都有自己的优点和缺点。您可以在这些书中阅读有关它们的更多信息。
通常(或者,我已经听说过,我不太了解其背后的原因),四叉树或八叉树更适合室外环境,而BSP树则更适合室内场景。使用四叉树还是八叉树的选择取决于您的世界有多平坦。如果Y轴上的变化很小,则使用八进制将是浪费的。Octree本质上是具有附加维度的四叉树。
最后,不要忽略Grid解决方案的简单性。许多人忽略了一个简单的网格有时足以解决他们的问题,甚至直接使用一个更复杂的解决方案。
使用网格只是将世界划分为均匀分布的区域,然后将实体存储在世界的适当区域中。然后,在给定位置的情况下,找到相邻实体将是遍历与搜索半径相交的区域的问题。
假设您的世界在XZ平面上的范围从(-1000,-1000)到(1000,1000)。例如,您可以将其划分为10x10的网格,如下所示:
var grid = new List<Entity>[10, 10];
然后,您可以将实体放入网格中其适当的单元格中。例如,具有XZ(-1000,-1000)的实体将落在像元(0,0)上,而具有XZ(1000,1000)的实体将落在像元(9,9)上。然后给定一个在世界上的位置和半径,您可以确定该“圆”与哪些单元格相交,并仅在这些单元格上进行迭代,并使用简单的double作为。
无论如何,请研究所有替代方案并选择似乎更适合您的游戏的替代方案。我承认我对这一主题仍然不了解,无法确定哪种算法最适合您。
编辑在另一个论坛上找到它,它可能会帮助您做出决定:
当绝大多数对象都位于网格正方形内并且分布相当均匀时,网格工作效果最佳。相反,当对象具有可变大小或聚集在较小区域时,四叉树起作用。
考虑到您对问题的模糊描述,我也倾向于使用网格解决方案(即假设单位较小且分布相当均匀)。
为了提高效率,请尝试使用非常便宜的包围盒检查法来舍弃不靠近目标单元的99%的“单元”。我希望您可以做到这一点,而无需在空间上构建数据。因此,如果所有单位都存储在平面数据结构中,则可以尝试从头到尾进行竞争,首先检查当前单位是否在感兴趣单位的边界框之外。
为感兴趣的单位定义一个超大的边界框,以便它可以安全地拒绝没有机会被视为“靠近”它的项目。排除边界框的检查可能比半径检查便宜。但是,在某些经过测试的系统上,情况并非如此。两者的表现几乎相同。经过大量辩论之后,对此进行了编辑。
首先:2D边界框剪辑。
// returns true if the circle supplied is completely OUTSIDE the bounding box, rectClip
bool canTrivialRejectCircle(Vertex2D& vCentre, WorldUnit radius, Rect& rectClip) {
if (vCentre.x + radius < rectClip.l ||
vCentre.x - radius > rectClip.r ||
vCentre.y + radius < rectClip.b ||
vCentre.y - radius > rectClip.t)
return true;
else
return false;
}
与类似的东西(在3D中)相比:
BOOL bSphereTest(CObject3D* obj1, CObject3D* obj2 )
{
D3DVECTOR relPos = obj1->prPosition - obj2->prPosition;
float dist = relPos.x * relPos.x + relPos.y * relPos.y + relPos.z * relPos.z;
float minDist = obj1->fRadius + obj2->fRadius;
return dist <= minDist * minDist;
}.
如果不轻易拒绝对象,则执行更昂贵,更准确的碰撞测试。但是,您只是在寻找接近度,因此球形测试适用于此,但仅适用于在微不足道的情况下幸存的1%的物体。
本文支持微不足道的拒绝框。 http://www.h3xed.com/programming/bounding-box-vs-bounding-circle-collision-detection-performance-as3
如果这种线性方法不能提供所需的性能,则可能需要分层数据结构,例如其他发布者一直在谈论的结构。R树值得考虑。它们支持动态变化。它们是空间世界的B树。
如果您可以避免的话,我只是不想让您麻烦地介绍这种复杂性。再加上随着对象每秒移动数次而使这种复杂数据结构保持最新状态的成本如何?
请记住,网格是一层深的空间数据结构。此限制意味着它不是真正可扩展的。随着世界规模的扩大,您需要覆盖的细胞数量也在增加。最终,该单元数本身成为性能问题。但是,对于特定大小的世界,它将在没有空间分区的情况下为您带来巨大的性能提升。
inside = (dot(p-p0, p-p0) <= r*r)
if
语句中检查失败)。也不太现实。但老实说,如果您开始优化这样的事情,那么您肯定是从错误的地方开始的。
我必须回答这个问题,因为我没有评论或支持的观点。正如Ciaran所描述的,对于99%提出此问题的人来说,边界框是解决方案。用编译的语言,它会在眨眼间拒绝100,000个无关单位。非蛮力解决方案涉及很多开销;如果使用较小的数字(例如少于1000个),则在处理时间方面将比蛮力检查更昂贵。而且他们将花费更多的编程时间。
我不确定问题中的“非常大”是什么意思,或者其他在这里寻找答案的人是什么意思。我怀疑上面的数字是保守的,可以乘以10。我个人对蛮力技术颇有偏见,并对它们的工作效果感到非常恼火。但是我不希望有人,例如拥有10,000个单位的人花一些时间来花哨的解决方案,而只需几行快速的代码就能解决问题。如果需要的话,他们以后总是可以花哨的。
另外,我要指出,边界球检查需要乘法,而边界框则不需要。就其本质而言,乘法的时间是加法和比较的几倍。语言,操作系统和硬件之间必定存在某种组合,其中球形检查比框形检查要快,但是在大多数地方和时间,框形检查都必须更快,即使球形确实拒绝了一些无关的单元也是如此。盒子接受。(在球形速度更快的地方,新版本的编译器/解释器/优化器很可能会改变这一点。)