# この単純な衝突ソルバーでオブジェクトが貫通するのはなぜですか？

7

ただし、シミュレーションでは球体が貫通するだけでなく、多数の球体が相互に積み重ねられている場合、小さな球体が大きな球体の内側にほぼ完全に収まります。すべての球が同じ半径と質量を持つようにすると、シミュレーションは適度に実行されます（最小の相互浸透で）。

シミュレーションの各球について、このメソッドは他のすべての球で呼び出されます。

``````/// <summary>
// Given 2 spheres with velocity, mass and size, evaluate whether
// a collision occured, and if so, excatly where, and move sphere 2
// at the contact point with sphere 1, and generate new velocities.
/// </summary>
private void SphereCollisionImplicit(Sphere sphere1, Sphere sphere2)
{
const float K_ELASTIC = 0.75f;

Vector3 relativepos = sphere2.Position - sphere1.Position;
float distance = relativepos.Length();
{
return; // No collision
}

// Add epsilon to avoid NaN.
distance += 0.000001f;

Vector3 relativeUnit = relativepos * (1.0f / distance);
Vector3 penetration = relativeUnit * (radii - distance);

// Adjust the spheres' relative positions
float mass1 = sphere1.Mass;
float mass2 = sphere2.Mass;

float m_inv = 1.0f / (mass1 + mass2);
float weight1 = mass1 * m_inv; // relative weight of sphere 1
float weight2 = mass2 * m_inv; // relative weight of sphere 2. w1+w2==1.0

sphere1.Position -= weight2 * penetration;
sphere2.Position += weight1 * penetration;

// Adjust the objects’ relative velocities, if they are
// moving toward each other.
//
// Note that we're assuming no friction, or equivalently, no angular momentum.
//
// velocityTotal = velocity of v2 in v1 stationary ref. frame
// get reference frame of common center of mass
Vector3 velocity1 = sphere1.Velocity;
Vector3 velocity2 = sphere2.Velocity;

Vector3 velocityTotal = velocity1 * weight1 + velocity2 * weight2;
Vector3 i2 = (velocity2 - velocityTotal) * mass2;
if (Vector3.Dot(i2, relativeUnit) < 0)
{
// i1+i2 == 0, approx
Vector3 di = Vector3.Dot(i2, relativeUnit) * relativeUnit;
i2 -= di * (K_ELASTIC + 1);
sphere1.Velocity = (-i2) / mass1 + velocityTotal;
sphere2.Velocity = i2 / mass2 + velocityTotal;
}
}
``````

``````sphere1.Position -= weight2 * penetration;
sphere2.Position += weight1 * penetration;
``````

5

あなたの問題は複数の衝突が原因だと思います。

3つの球A、B、Cについて考えます。

Aがそこに座っています。

BがAをヒット。

CがBにヒットします。BがAにナッジされます。

はい、でも今はCがAとBの両方に対して位置を修正します。私はあなたの意味を理解していますが、3つの球で作成した例はうまくいくようです-それが本当に問題なのかどうかはわかりません。また、この反復法は、ほとんどのゲーム物理シミュレーションが衝突を解決する方法であると私は信じています。繰り返しますが、私は間違っている可能性があります。たぶん、この方法で一度に1つずつ衝突を解決すると、連続衝突検出を使用する場合にのみ相互侵入が防止されます。
Olhovsky、

これが実際の理由かもしれません。ブランドンは同様のことを提案しました。+1は、正確な理由であるかどうかにかかわらず、有用な貢献を示します。
Olhovsky、

5

このコードは、2つの球の間の衝突を取り、それらの質量と速度に基づいて球を移動しています。

これは、青いボールと緑の壁の間に貫通がある理由を示す非常に基本的な図です。

この例では、ボールの中心は壁の端から5フィートです。ボールは毎秒1フィートで動き、毎秒1フレームのフレームレートで走っています。フレーム4では貫通はありませんが、フレーム5には貫通があります。フレーム5が衝突することを知る方法はないので、壁に到達したときに停止とは言えません。代わりに、ボールが部分的に壁の内側にあるかどうか、すべてのフレームをチェックします。ある場合は、ペネトレーションの量だけ戻し、反射速度を適用します。これは、ほとんどの衝突検出アルゴリズムが機能する方法です。別の方法として、特定の方向に衝突するまでの距離を各フレームで確認することもできます。これの問題は、チェックする方向を知る必要があることです。これはあなたの状況では機能しないので、あなたが取っているルートは正しいものです。

このアプローチの欠点は、おそらく経験しているジッターです。このスクリプトが、最後の衝突に基づいて移動するようにボールに指示する場合、別のボールに侵入するように移動している可能性があり、その後、最初のボールと衝突するようにボールを送り返します。前後にジャンプさせる。これに役立つようにオブジェクトの質量を変更できます。これにより、ジャンプが大幅に減少するか、最初に衝突する速度が低下します。

Olhovsky、

ああ、私はあなたがローレンの答えと同様に、複数のボールが互いに押し出されるために複数の衝突が正しく解決されないと言っていると思います-それは正しいですか？
Olhovsky、

そう、それはジッターであり、これもまた彼らが正しく衝突しない原因になります
ブランドン

まあ、そうでもない。不安定性！=相互侵入。オブジェクトは、相互侵入せずに（侵入解決のために）ジッターする可能性があります。とにかく+1 :)
Olhovsky、

1

SphereCollisionImplicit（）は、球が移動した後に呼び出されます。その動きは衝突と相互侵入で終わった可能性があり、この方法はそれを解決します。

1. コードの他の場所では、位置は速度によって更新されます。

2. 次に、このメソッドが呼び出されて、衝突/侵入が発生したかどうかが確認されます。

3. もしそうなら、それはそれらを分離し、それらの速度を反映/変更します。

4. 次のフレームでは、位置は新しい/変更された速度で更新されます... goto 1。

Olhovsky、

ステップ1と4はペネトレーションを引き起こす可能性があり、ステップ3はペネトレーションが発生した場合にそれを削除します。
スティーブH

この質問の要点は、浸透が完全に取り除かれているわけではないということです。その理由については、他の回答を参照してください。
Olhovsky、

1

ボールの位置と速度が変更されるたびに、ループの初期にチェックされた他のボールと衝突したかどうかを確認する必要があるため、相互侵入があります。たとえば、3つのボールがあるとします。

スタート1.ボール#Aがボール#Bに対してチェックされ、衝突はない

1. ボール#Aはボール#Cに対してチェックされ、衝突はありません

2. ボール#Bはボール#Aに対してチェックされ、衝突はありません

3. ボール#Bがボール#Cに対してチェックされ、衝突が見つかりました!! ボール#Bと#Cが移動します。

4. ボール#Cはボール#Aに対してチェックされ、衝突はありません

5. ボール#Cはボール#Bに対してチェックされ、衝突はありません

できた

したがって、ステップ2でボール#Bがボール#Aに移動し、相互侵入が発生した可能性があります。

1つのフレームでいくつのリチェットを処理しますか？また、各リチェットの後にすべての壁に対して再チェックする必要がありますか？

スタート1.ボール#Aがボール#Bに対してチェックされ、衝突はない

1. ボール#Aはボール#Cに対してチェックされ、衝突はありません

2. ボール#Bはボール#Aに対してチェックされ、衝突はありません

3. ボール#Bがボール#Cに対してチェックされ、衝突が見つかりました!! ボール#Bと#Cが移動します。

4. #Bおよび#Cループを再起動します...

5. ボール#Bがボール#Aに対してチェックされ、衝突が見つかりました!! ボール#Bと#Aが移動する

6. #Bおよび#Aループを再起動します...

7. ボール#Aはボール#Bに対してチェックされ、衝突はありません

8. ボール#Aがボール#Cに対してチェックされ、衝突が見つかりました!! ボール#Aと#Cが移動

9. #Aおよび#Cループを再始動します。

10. ボール#Aはボール#Bに対してチェックされ、衝突はありません

11. ボール#Aはボール#Cに対してチェックされ、衝突はありません

12. ボール#Bはボール#Aに対してチェックされ、衝突はありません

13. ボール#Bがボール#Cに対してチェックされ、衝突が見つかりました!! ボール#Bと#Cが移動

14. #Bおよび#Cループを再始動します

15. ボール#Bはボール#Aに対してチェックされ、衝突はありません

16. ボール#Bはボール#Cに対してチェックされ、衝突はありません

17. ボール#Cはボール#Aに対してチェックされ、衝突はありません

18. ボール#Cはボール#Bに対してチェックされ、衝突なし

Olhovsky