2017-01-27 18 views
2

私はBox2Dに似た非常に薄くて単純な衝突者を書こうとしています。すべての物理、回転などはありません。私はこの両方を行って、個人的にこれらの事柄の内部の仕組みを単に学ぶだけです。Box2Dのような円のたくさんの衝突

私がしようとしているのは、円と線を衝突させ、それらが互いに埋め込まれないようにすることです。

Box2Dはこれをほぼ完璧に - 非常に小さなオーバーラップです!しかし、自分のシンプルなシミュレータを書くと、重複が多くなります:picture of overlapped circles

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; 
} 
} 

答えて

2

は非常に難しい問題です近似ソルバから来る誤差(実世界の物理は積分に基づいて微小時間ステップを決定するが、シミュレーションでは通常1秒間に60回しか解くことができない)。

Box2D's constraint solver loop, located in b2island.cppを見てみましょう:すべてのワールドステップで、衝突レゾルバは一度だけ実行されるだけではありません。それは通常のテストケースでは通常6または8に設定されているvelocityIterations回を繰り返します。それはあなたが同様に行う必要がありますです。

+1

ありがとうございます。私は実際にこれを試しましたが、より良い結果が得られます。ちょうど非常に非効率的なようです。私はBox2Dがこれをしたことを知らなかった。私はそれを見て回ったが、反復を見逃した。それは私が知る必要があったすべてです。 – Raptisoft

+0

イテレーションあたりのメジアンエラーを調べ、イテレーションによってエラーがどのくらい軽減されるかを確認することをお勧めします。半暗黙のオイラーを使用しているので、反復の量とエラー削減の間に線形相関があるはずです。 – Domi

+0

また、「弾丸」(非常に高速に動くオブジェクト)の特殊なケースでのみ使用される連続ソルバーを引用していたことに気付きました。通常、オブジェクトには個別のソルバーステップしかありません。これは['b2island :: solve'](https://github.com/erincatto/Box2D/blob/374664b2a4ce2e7c24fbad6e1ed34bebcc9ab6bc/Box2D/Box2D/Dynamics/b2Island.cpp#L260)にあります。 )。彼らはエラーをチェックするのではなく、むしろ 'velocityIterations'回だけを繰り返すことになります。通常は6か8です。私の答えが更新されました。 - それについては申し訳ありません! – Domi