0

複数の円が2D空間を移動するシミュレーションがあります。

それらの間には衝突検出があり、弾性衝突は95%の時間で機能します。ただし、2つのボールが互いにぶつかると、ボールが互いにくっついて重なり合うことがあり、多くの場合、互いにくっついている間に互いに周回します。

この問題を解決する方法がわかりません。

私の衝突管理機能は次のようになります。

void manageCollision(Particle particleA, Particle particleB)
{
    float distanceX = particleA.Position.X - particleB.Position.X;
    float distanceY = particleA.Position.Y - particleB.Position.Y;
    double collisionAngle = Math.Atan2(distanceY, distanceX);
    double pA_magnitude = Math.Sqrt(particleA.Velocity.X * particleA.Velocity.X + particleA.Velocity.Y * particleA.Velocity.Y);
    double pB_magnitude = Math.Sqrt(particleB.Velocity.X * particleB.Velocity.X + particleB.Velocity.Y * particleB.Velocity.Y);
    double pA_direction = Math.Atan2(particleA.Velocity.Y, particleA.Velocity.X);
    double pB_direction = Math.Atan2(particleB.Velocity.Y, particleB.Velocity.X);
    double pA_newVelocityX = pA_magnitude * Math.Cos(pA_direction - collisionAngle);
    double pA_newVelocityY = pA_magnitude * Math.Sin(pA_direction - collisionAngle);
    double pB_newVelocityX = pB_magnitude * Math.Cos(pB_direction - collisionAngle);
    double pB_newVelocityY = pB_magnitude * Math.Sin(pB_direction - collisionAngle);
    double pA_finalVelocityX = ((particleA.Mass - particleB.Mass) * pA_newVelocityX + (particleB.Mass + particleB.Mass) * pB_newVelocityX) / (particleA.Mass + particleB.Mass);
    double pB_finalVelocityX = ((particleA.Mass + particleA.Mass) * pA_newVelocityX + (particleB.Mass - particleA.Mass) * pB_newVelocityX) / (particleA.Mass + particleB.Mass);
    double pA_finalVelocityY = pA_newVelocityY;
    double pB_finalVelocityY = pB_newVelocityY;
    particleA.Velocity = new Vector2((float)(Math.Cos(collisionAngle) * pA_finalVelocityX + Math.Cos(collisionAngle + Math.PI / 2) * pA_finalVelocityY), (float)(Math.Sin(collisionAngle) * pA_finalVelocityX + Math.Sin(collisionAngle + Math.PI / 2) * pA_finalVelocityY));
    particleB.Velocity = new Vector2((float)(Math.Cos(collisionAngle) * pB_finalVelocityX + Math.Cos(collisionAngle + Math.PI / 2) * pB_finalVelocityY), (float)(Math.Sin(collisionAngle) * pB_finalVelocityX + Math.Sin(collisionAngle + Math.PI / 2) * pB_finalVelocityY));
}

各ボールまたはパーティクルは、ランダムな質量と半径でスポーンします。

この関数は、次のような更新タイプのメソッド内で呼び出されます。

Particle pA = particles[i];
for (int k = i + 1; k < particles.Count(); k++)
{
    Particle pB = particles[k];
    Vector2 delta = pA.Position - pB.Position;
    float dist = delta.Length();

    if (dist < particles[i].Radius + particles[k].Radius && !particles[i].Colliding && !particles[k].Colliding)
    {
        particles[i].Colliding = true;
        particles[k].Colliding = true;
        manageCollision(particles[i], particles[k]);
        particles[i].initColorTable(); // Upon collision, change the color
        particles[k].initColorTable();
        totalCollisions++;
    }
    else
    {
        particles[i].Colliding = false;
        particles[k].Colliding = false;
    }
}
4

1 に答える 1

1

この状況は、離散計算と期間の大きなステップサイズに起因します。

時間間隔dtでオブジェクトを観察すると、2つの円の交差を観察して衝突メソッドを呼び出すことができますが、次のタイムステップでは、前のステップでの衝突後に異なる方向に進んでいるにもかかわらず、オブジェクトがオーバーラップする可能性があります。

この影響を減らすには、時間ステップサイズを小さくして、オブジェクト間のオーバーラップ率を減らすことができます。より複雑な解決策として、すべてのステップで衝突したオブジェクトのリストを保持できます。反復中に、現在の交差する円に前のステップで「問題」があったかどうかを確認できます。

于 2013-03-19T06:46:17.153 に答える