1

現在、次を使用して、互いに跳ね返る 2 つの球を計算しています。これは、3D オブジェクトを使用する 2D ピンポン ゲームで使用されています (頭を 3D に巻き付けようとしています)。ほとんどすべてが正常に機能しますが、時々 (通常、X または Y の速度が同じ方向に移動し、一方が他方よりも速い場合)、物理は奇妙なことを行います。

返されたフロートは、ボールが衝突したときに再生されるサウンドを変更するために使用する質量の違いです。誰でも私の計算の間違いを見ることができますか:

internal float ResolveCollision(Ball otherBall)
{
    if (otherBall == this) { return 0f; }
    if (this.GetBoundingSphere().Intersects(otherBall.GetBoundingSphere()))
    {
        // Attempt to step the balls back so they are just barely touching
        Vector3 dd = Position - otherBall.Position;
        dd.Normalize();
        Position += dd / 2;
        otherBall.Position -= dd / 2;

        ///http://williamecraver.wix.com/elastic-equations

        Vector3 V1 = Velocity;
        Vector3 P1 = Position;
        float M1 = Mass;
        float A1 = getMovementAngle(V1.X, V1.Y);

        Vector3 V2 = otherBall.Velocity;
        Vector3 P2 = otherBall.Position;
        float M2 = otherBall.Mass;
        float A2 = getMovementAngle(V2.X, V2.Y);

        float CA = getContactAngle(P1, P2);

        // Recalculate x and y components based of a rotated axis, having the x axis parallel to the contact angle.
        Vector3 V1XR = V1 * (float)Math.Cos(A1 - CA);
        Vector3 V1YR = V1 * (float)Math.Sin(A1 - CA);

        Vector3 V2XR = V2 * (float)Math.Cos(A2 - CA);
        Vector3 V2YR = V2 * (float)Math.Sin(A2 - CA);

        //Now solve the x components of the velocity as if they were in one dimension using the equation;
        Vector3 V1f = (V1 * (M1 - M2) + 2 * M2 * V2) / (M1 + M2);
        Vector3 V2f = (V2 * (M2 - M1) + 2 * M1 * V1) / (M1 + M2);

        Vector3 V1fXR = (V1 * (float)Math.Cos(A1 - CA) * (M1 - M2) + 2 * M2 * V2 * (float)Math.Cos(A2 - CA)) / (M1 + M2);
        Vector3 V2fXR = (V2 * (float)Math.Cos(A2 - CA) * (M2 - M1) + 2 * M1 * V1 * (float)Math.Cos(A1 - CA)) / (M1 + M2);

        //Now find the x and y values for the un-rotated axis by equating for the values when the axis are rotated back.
        Vector3 V1fX = V1fXR * (float)Math.Cos(CA) + V1YR * (float)Math.Cos(CA + MathHelper.PiOver2);
        Vector3 V1fY = V1fXR * (float)Math.Sin(CA) + V1YR * (float)Math.Sin(CA + MathHelper.PiOver2);
        Vector3 V2fX = V2fXR * (float)Math.Cos(CA) + V2YR * (float)Math.Cos(CA + MathHelper.PiOver2);
        Vector3 V2fY = V2fXR * (float)Math.Sin(CA) + V2YR * (float)Math.Sin(CA + MathHelper.PiOver2);

        // Add it all up
        Vector3 nV1 = V1fX + V1fY;
        Vector3 nV2 = V2fX + V2fY;

        ///////////////////////////////////////////
        // Correct Velocity & Move apart
        //////////////////////////////////////////
        Velocity = v3check(nV1, MAXSPEED, -MAXSPEED);
        otherBall.Velocity = v3check(nV2, MAXSPEED, -MAXSPEED);

        // Step the balls forward (by there Velocity) just a bit so they are no longer touching
        Position += Velocity * _lastDT * .25f;
        otherBall.Position += otherBall.Velocity * otherBall._lastDT * .25f;

        return BMDMath.toFloat(Mass - otherBall.Mass);
    }

    return 0f;
}

いくつかの角度を変換する次のヘルパー メソッドがあります (これが問題の原因である可能性があります。

private static float getMovementAngle(double vx, double vy)
{
    return MathHelper.ToDegrees((float)Math.Atan2(vy, vx));
}


private static float getContactAngle(Vector3 o1, Vector3 o2)
{
    Vector3 d = o1 - o2;
    return MathHelper.ToDegrees((float)Math.Atan2(d.Y, d.X));
}
4

2 に答える 2

1

Nico Shertler による回答は、ベクトルの観点から問題を設定する優れた仕事をしており、優れた図があります。ただし、いずれかのボールの速度がゼロの場合、コード例の物理部分は機能しません。V1 または V2 がそれぞれゼロの場合、v1Length または v2Length が 0 になるため、ゼロ除算エラーが生成されます。

代わりに、物理部分は、ウィキペディアの弾性衝突に関する記事 ( http://en.wikipedia.org/wiki/Elastic_collision#One-dimensional_Newtonian )の式に置き換えることができます。したがって、次のようになります。

var totalMass = this.Mass + otherBall.Mass;
var v1ParallelNew = (v1Parallel * (this.Mass - otherBall.Mass) + 2*otherBall.Mass * v2Parallel) / totalMass;
var v2ParallelNew = (v2Parallel * (otherBall.Mass - this.Mass) + 2*this.Mass * v1Parallel) / totalMass;
v1Parallel = v1ParallelNew;
v2Parallel = v2ParallelNew;

次に、彼のコード例の残りの部分のように、平行成分と直交成分を再結合できます。

もう 1 つ重要な考慮事項があります。速度のみを変更し、コリジョンが検出された時点でボールがコリジョンの外に移動されていない場合、ボールの速度によってボールが互いに遠ざかっていても、次のフレームで重なっている (衝突している) 可能性があります。これにより、コードが互いに速度を逆にするため、それらがくっつきます。この状態を回避するには、少なくとも 1 つのボールが実際に他のボールに向かって「平行」方向に移動している場合にのみ、速度を変更します。例えば:

if (Vector.Dot(collisionNormal, V1) > 0 || Vector.Dot(collisionNormal, V2) < 0)
{
    // do the physics code here
}

この条件付きチェックにより、最初の衝突解決が速度に適用された後もボールがまだ重なっている場合でも、ボールは互いに遠ざかり続けることができます。また、重なった状態で発生したボールを分離することもできます。

于 2013-12-02T07:33:07.493 に答える