この質問が初めての場合は、以下の更新前の部分を最初に読んでから、この部分を読むことをお勧めします。 ただし、問題の統合は次のとおりです。
基本的に、衝突の順序と衝突グループが重要なグリッド空間分割システムを備えた衝突検出および解決エンジンがあります。一度に1つのボディを移動し、衝突を検出してから衝突を解決する必要があります。すべてのボディを一度に移動し、可能な衝突ペアを生成すると、明らかに高速になりますが、衝突の順序が尊重されないため、解像度が壊れます。一度に1つのボディを動かすと、ボディに衝突をチェックさせなければならず、^ 2の問題になります。グループをミックスに入れると、多くのボディで非常に速く非常に遅くなる理由を想像できます。
更新:私はこれに本当に一生懸命取り組みましたが、何も最適化することができませんでした。
Willによって記述された「ペインティング」の実装に成功し、グループをビットセットに変更しましたが、非常に小さなスピードアップです。
また、大きな問題を発見しました。私のエンジンは衝突順序に依存しています。
私はユニークな衝突ペア生成の実装を試みました。これは間違いなくすべてを高速化しますが、衝突の順序を破りました。
説明させてください:
私の元の設計(ペアを生成しない)で、これが起こります:
- 単一の体が動く
- 移動した後、セルを更新し、衝突したボディを取得します
- それが解決する必要があるボディとオーバーラップする場合、衝突を解決する
つまり、ボディが移動して壁(または他のボディ)にぶつかった場合、移動したボディのみが衝突を解決し、他のボディは影響を受けません。
これが私が望む行動です。
物理エンジンでは一般的ではないことを理解していますが、レトロスタイルのゲームでは多くの利点があります。
通常のグリッド設計(一意のペアを生成)では、これが起こります:
- すべての体が動く
- すべてのボディが移動した後、すべてのセルを更新します
- 一意の衝突ペアを生成する
- 各ペアについて、衝突の検出と解決を処理します
この場合、同時移動により2つのボディがオーバーラップする可能性があり、それらは同時に解決します。これにより、ボディは事実上「互いに押し合い」、複数のボディとの衝突安定性が損なわれます。
この動作は物理エンジンでは一般的ですが、私の場合は受け入れられません。
また、別の問題も発見しました。これは重大です(実際の状況では起こりそうにない場合でも)。
- グループA、B、Wのボディを検討する
- AはWとAに衝突して解決します
- BはWとBに対して衝突して解決します
- AはBに対して何もしません
- BはAに対して何もしません
多くのAボディとBボディが同じセルを占有する場合があります-その場合、ボディ間で不必要な反復が多く、相互に反応してはなりません(または衝突を検出するだけで解決しない) 。
同じセルを占める100体の場合、100 ^ 100回の反復です!これは、一意のペアが生成されていないために発生しますが、一意のペアを生成できません。そうしないと、望ましくない動作が発生します。
この種の衝突エンジンを最適化する方法はありますか?
これらは尊重されなければならないガイドラインです:
衝突の順序は非常に重要です!
- ボディは一度に1つずつ移動し、次に衝突を1つずつ確認し、移動後に1つずつ解決する必要があります。
ボディには3つのグループビットセットが必要です
- グループ:ボディが属するグループ
- GroupsToCheck:ボディが衝突を検出する必要があるグループ
- GroupsNoResolve:ボディが衝突を解決してはならないグループ
- 衝突を検出するだけで解決しない場合があります
事前更新:
はじめに:このボトルネックを最適化する必要はないことを認識しています-エンジンは既に非常に高速です。しかし、楽しくて教育的な目的で、エンジンをさらに高速にする方法を見つけたいと思っています。
柔軟性と速度に重点を置いて、汎用C ++ 2D衝突検出/応答エンジンを作成しています。
そのアーキテクチャの非常に基本的な図を次に示します。
基本的には、メインクラスWorld
所有している、の(メモリ管理)ResolverBase*
、SpatialBase*
およびvector<Body*>
。
SpatialBase
は、幅広いフェーズの衝突検出を扱う純粋な仮想クラスです。
ResolverBase
衝突解決を扱う純粋な仮想クラスです。
ボディは、ボディ自体が所有するオブジェクトWorld::SpatialBase*
と通信しSpatialInfo
ます。
現在Grid : SpatialBase
、1つの空間クラスがあります。これは、基本的な固定2Dグリッドです。独自の情報クラスがありGridInfo : SpatialInfo
ます。
そのアーキテクチャは次のとおりです。
Grid
クラスはの2D配列を所有していますCell*
。Cell
このクラスは、(所有していない)のコレクションが含まBody*
:vector<Body*>
セル内にあるすべての遺体が含まれています。
GridInfo
オブジェクトには、ボディが含まれるセルへの非所有ポインターも含まれます。
前述したように、エンジンはグループに基づいています。
Body::getGroups()
std::bitset
本体が属するすべてのグループのを返します。Body::getGroupsToCheck()
std::bitset
ボディが衝突をチェックする必要があるすべてのグループのを返します。
ボディは単一のセル以上を占有できます。GridInfoは、占有セルへの非所有ポインターを常に保存します。
単一のボディが移動すると、衝突検出が発生します。すべてのボディは、軸に沿った境界ボックスであると想定しています。
広域位相衝突検出の仕組み:
パート1:空間情報の更新
それぞれについてBody
body
:
- 一番左上の占有セルと一番右下の占有セルが計算されます。
- 前のセルと異なる場合、
body.gridInfo.cells
クリアされ、ボディが占めるすべてのセルで埋められます(左上のセルから右下のセルへの2Dループ)。
body
現在、どのセルを占有しているかを知ることが保証されています。
パート2:実際の衝突チェック
それぞれについてBody
body
:
body.gridInfo.handleCollisions
と呼ばれます:
void GridInfo::handleCollisions(float mFrameTime)
{
static int paint{-1};
++paint;
for(const auto& c : cells)
for(const auto& b : c->getBodies())
{
if(b->paint == paint) continue;
base.handleCollision(mFrameTime, b);
b->paint = paint;
}
}
void Body::handleCollision(float mFrameTime, Body* mBody)
{
if(mBody == this || !mustCheck(*mBody) || !shape.isOverlapping(mBody->getShape())) return;
auto intersection(getMinIntersection(shape, mBody->getShape()));
onDetection({*mBody, mFrameTime, mBody->getUserData(), intersection});
mBody->onDetection({*this, mFrameTime, userData, -intersection});
if(!resolve || mustIgnoreResolution(*mBody)) return;
bodiesToResolve.push_back(mBody);
}
その後、衝突はすべてのボディで解決され
bodiesToResolve
ます。それでおしまい。
それで、私はかなり長い間、この広い位相の衝突検出を最適化しようとしています。現在のアーキテクチャ/セットアップ以外の何かを試みるたびに、何かが計画通りに進まないか、または後で間違っていることが証明されたシミュレーションについて仮定します。
私の質問は、どのように衝突エンジンの広範なフェーズを最適化できますか?
ここで適用できる魔法のC ++最適化の種類はありますか?
パフォーマンスを向上させるために、アーキテクチャを再設計できますか?
- 実際の実装:SSVSCollsion
- Body.h、 Body.cpp
- World.h、 World.cpp
- Grid.h、 Grid.cpp
- Cell.h、 Cell.cpp
- GridInfo.h、 GridInfo.cpp
最新バージョンのコールグラインド出力:http ://txtup.co/rLJgz
getBodiesToCheck()
は5462334回呼び出され、プロファイリング時間全体の35,1%を要しました(命令読み取りアクセス時間)