私はBox2Dに似た非常に薄くて単純な衝突者を書こうとしています。すべての物理、回転などはありません。私はこの両方を行って、個人的にこれらの事柄の内部の仕組みを単に学ぶだけです。Box2Dのような円のたくさんの衝突
私がしようとしているのは、円と線を衝突させ、それらが互いに埋め込まれないようにすることです。
Box2Dはこれをほぼ完璧に - 非常に小さなオーバーラップです!しかし、自分のシンプルなシミュレータを書くと、重複が多くなります:
Box2Dを使って同じシミュレーションを実行すると(これは画面の中央にある点を追うすべての円です)、ALLではオーバーラップは表示されません。擬似コードで
、これは私が何をすべきかです:
For each Circle In List:
Determine who will collide with the circle in next step
Sort collisions by closest first
For each possible collision:
Add the unembed vector to the Circle's movement vector
...and then:
For each Circle In List:
At the movement to the circle
円は他の何かに押し込まれませんのであれば、これも完璧に動作します。しかし、物事が盛り上がると、それはうまくいかず、私はなぜ知っているのですか?アンブレッドは単に蓄積し、後のサークルは以前のサークルに埋め込まれず、シミュレーションの最後には。莫大な意味合いを持つ。
ここで私は混乱しています: 近いうちに、Box2Dは同じ方法で正確に動作します。可能な衝突を取得し、互いに衝突しないようにします。しかし、Box2Dは決して今までと同じように重なり合うことはありません。
誰かが私がここで逃したステップを教えてもらえますか? (Box2Dはこれを行うようには見えず、コードを軽く速く保ちながら理解したいと思っています)。
ありがとうございます!
下記関連のある実際のコード:衝突の基本的な数学に加えて、あなたは数学の蓄積を固定ではるかに熱心に仕事をしなければならない、ので、多くのオブジェクト間の衝突を解決
aO->mPos = x,y of object
aO->mMove = x,y of movement this step
aO->mRadius = radius of object
aO->MovingBound() = bound of object including the move
void Step()
{
EnumList(MCObject,aO,mMovingObjectList)
{
mTree.GetAllNearbyObjects(aO->MovingBound().Expand(aO->mRadius/4),&aHitList);
aHitList-=aO; // Remove self from list
if (aHitList.GetCount()>0)
{
// Sort the collisions by closest first
if (mSortCollisions)
{
// Snip, took this out for clarity...
// It just sorts aHitList by who is closest
// to the current object
}
// Apply the unembedding
EnumList(MCObject,aO2,aHitList) CollideObjectObject(aO,aO2);
}
}
// Do the actual moves
EnumList(MCObject,aO,mMovingObjectList)
{
mTree.Move(aO->mProxy,aO->Bound(),aO->mMove);
aO->mPos+=aO->mMove;
aO->mMove=0;
}
}
void CollideObjectObject(MCObject* theO1, MCObject* theO2)
{
float aOverlap=gMath.DistanceSquared(theO1->mPos+theO1->mMove,theO2->mPos+theO2->mMove);
float aMixRadius=theO1->mRadius+theO2->mRadius;
if (aOverlap<=aMixRadius*aMixRadius)
{
Point aUnembed=(theO1->mPos-theO2->mPos);
float aUnembedLength=aMixRadius-sqrt(aOverlap);
aUnembed.SetLength(aUnembedLength);
float aMod=.5f;
if (theO2->mCollideFlags&COLLIDEFLAG_STATIONARY) aMod=1.0f;
theO1->mMove+=aUnembed*aMod;
}
}
ありがとうございます。私は実際にこれを試しましたが、より良い結果が得られます。ちょうど非常に非効率的なようです。私はBox2Dがこれをしたことを知らなかった。私はそれを見て回ったが、反復を見逃した。それは私が知る必要があったすべてです。 – Raptisoft
イテレーションあたりのメジアンエラーを調べ、イテレーションによってエラーがどのくらい軽減されるかを確認することをお勧めします。半暗黙のオイラーを使用しているので、反復の量とエラー削減の間に線形相関があるはずです。 – Domi
また、「弾丸」(非常に高速に動くオブジェクト)の特殊なケースでのみ使用される連続ソルバーを引用していたことに気付きました。通常、オブジェクトには個別のソルバーステップしかありません。これは['b2island :: solve'](https://github.com/erincatto/Box2D/blob/374664b2a4ce2e7c24fbad6e1ed34bebcc9ab6bc/Box2D/Box2D/Dynamics/b2Island.cpp#L260)にあります。 )。彼らはエラーをチェックするのではなく、むしろ 'velocityIterations'回だけを繰り返すことになります。通常は6か8です。私の答えが更新されました。 - それについては申し訳ありません! – Domi