最大30個のオブジェクトのみの場合、フレームごとに2回以上同じ2つのペアを相互にチェックしないことを除いて、多くの最適化は必要ありません。以下のコードサンプルでカバーします。しかし、物理エンジンが使用するさまざまな最適化に興味がある場合は、この投稿の残りの部分を読み進めてください。
必要なのは、Octree(3Dゲームの場合)またはQuadtree(2Dゲームの場合)などの空間分割実装です。これらは世界をサブセクションに分割し、各サブセクションは、同じサイズでさらに分割されて、最小サイズに分割されます。これにより、他のどのオブジェクトが別のオブジェクトと同じ世界の領域にあるかを非常にすばやく確認でき、確認する必要がある衝突の量が制限されます。
空間分割に加えて、物理オブジェクトごとにAABB(軸に沿った境界ボックス)を作成することをお勧めします。これにより、あるオブジェクトのAABBを別のオブジェクトに対してチェックできます。これは、オブジェクト間の詳細なポリゴン単位のチェックよりもはるかに高速です。
複雑なまたは大きな物理オブジェクトの場合は、これをさらに一歩進めることができます。物理メッシュ自体を細分割して、2つのオブジェクトのAABBが重複している場合にのみチェックできる独自のAABBを各サブシェイプに与えることができます。
ほとんどの物理エンジンは、静止した物理ボディのアクティブな物理シミュレーションを無効にします。物理ボディが非アクティブ化されると、フレームごとに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コンテナを変更しないでください。変更すると、衝突チェックが失敗する可能性があります。これは常識のように聞こえるかもしれませんが、衝突に反応して物事が予期しない変化を引き起こすことがどれほど簡単であるかに驚くでしょう。たとえば、衝突によって、衝突しているオブジェクトの1つが十分に位置を変更して、チェックしていたOctreeノードのAABBからそれらを削除した場合、そのコンテナが変更される可能性があります。これを解決するには、チェック中に発生するすべての衝突イベントのリストを保持し、すべてのチェックが完了した後、リストを実行して衝突イベントを送信することをお勧めします。