最多只能容纳30个对象,除了每帧不对相同的两对对象进行相互检查之外,您根本不需要进行太多优化。下面的代码示例将涵盖其中。但是,如果您对物理引擎将使用的不同优化感兴趣,请继续阅读本文的其余部分。
您将需要一个空间分区实现,例如Octree(用于3D游戏)或Quadtree(用于2D游戏)。这些将世界划分为多个子区域,然后将每个子区域在同一庄园中进一步划分,直到将其细分为最小大小为止。这样,您可以非常快速地检查哪些其他对象与另一个对象在同一区域,从而限制了必须检查的碰撞量。
除了空间分区之外,我还建议为您的每个物理对象创建一个AABB(与轴对齐的边界框)。这使您可以对照一个对象检查一个对象的AABB,这比对象之间进行详细的按多边形检查要快得多。
对于复杂的或大型的物理对象,可以采取进一步的措施,您可以细分物理网格本身,为每个子形状赋予自己的ABB,只有两个对象的ABB重叠时,您才能进行检查。
一旦大多数物理引擎停止运行,它们就会停用其对物理主体的主动物理模拟。停用物理实体时,只需在每个帧上检查与它的AABB的碰撞,如果任何物体与AABB碰撞,则它将重新激活并进行更细化的碰撞检查。这样可以缩短仿真时间。
同样,许多物理引擎都使用“模拟岛”,这是一组紧密靠在一起的物理物体组合在一起的地方。如果模拟岛中的所有内容均处于静止状态,则模拟岛本身将停用。仿真岛的好处是,一旦岛处于非活动状态,其中的所有物体都可以停止检查碰撞,并且每帧的唯一检查就是查看是否有物体进入了岛的AABB。只有一旦有东西进入岛上的AABB,岛上的每个物体都需要检查碰撞。如果模拟岛中的任何物体开始再次自行移动,它也会重新激活。如果物体远离组中心的距离足够远,则会将其从岛上移开。
最后,剩下的东西是这样的(用伪代码):
// Go through each leaf node in the octree. This could be more efficient
// by keeping a list of leaf nodes with objects in it.
for ( node in octreeLeafNodes )
{
// We only need to check for collision if more than one object
// or island is in the bounds of this octree node.
if ( node.numAABBsInBounds > 1)
{
for ( int i = 0; i < AABBNodes.size(); ++i )
{
// Using i+1 here allows us to skip duplicate checks between AABBS
// e.g (If there are 5 bodies, and i = 0, we only check i against
// indexes 1,2,3,4. Once i = 1, we only check i against indexes
// 2,3,4)
for ( int j = i + 1; j < AABBNodes.size(); ++j )
{
if ( AABBOverlaps( AABBNodes[i], AABBNodes[j] ) )
{
// If the AABB we checked against was a simulation island
// then we now check against the nodes in the simulation island
// Once you find overlaps between two actual object AABBs
// you can now check sub-nodes with each object, if you went
// that far in optimizing physics meshes.
{
}
}
}
}
我还建议不要在这样的循环中包含太多的循环,上面的示例只是为了让您有一个主意,我将其分解为多个函数,这些函数可以为您提供与上述功能相同的功能。
另外,请确保在循环访问AABBNodes容器时不要更改它,因为这可能意味着错过了碰撞检查。这听起来像是常识,但是您会感到惊讶的是,让事物对碰撞做出反应会导致您意料之外的变化是多么容易。例如,如果碰撞导致碰撞对象之一改变位置足以将其从您正在检查的Octree节点的AABB中移除,则它可能会更改该容器。为了解决这个问题,我建议保留检查期间发生的所有碰撞事件的列表,然后在所有检查完成后遍历该列表并发出所有碰撞事件。