4

私は物事を立方体/無限質量の長方形と衝突する単一の立方体と次のコードにまで単純化しました:

問題は、ボックスが回転しすぎて回転しすぎてスタックする傾向があり、バイナリ検索が含まれている場合は、ヒットして回転するだけです。

助けてくれてありがとう。

/// <summary>
/// Projects an abstract 1D line "perpendicular" to the axis, 
/// stretching across the width of the model,
/// measured from that axis.
/// </summary>
/// <param name="Axis"></param>
/// <param name="Min"></param>
/// <param name="Max"></param>
protected virtual void ProjectToAxis(Vector2 Axis, IMotionData motionData, out double Min, out double Max)
{
    Double DotP = Axis.Dot(motionData.PositionGS + (this.Vertices[0].Position * this.Model.Scale).Rotate(motionData.RotationGS));

    Min = Max = DotP;

    for (int t = 1; t < this.Vertices.Count(); ++t)
    {
        DotP = Axis.Dot(motionData.PositionGS + (this.Vertices[t].Position * this.Model.Scale).Rotate(motionData.RotationGS));

        Min = Math.Min(DotP, Min);
        Max = Math.Max(DotP, Max);
    }
}


/// <summary>
/// Projects two imaginary lines even with each edge,
/// equal to the width of each object while looking at
/// that edge, then checks to see if they intersect.
/// </summary>
/// <param name="B1"></param>
/// <param name="B2"></param>
/// <returns></returns>
public static bool DetectCollision(Body B1, Body B2, Double elapsedSeconds)
{
    CollisionData collisionInfo = new CollisionData();
    double lowestDistance = double.MaxValue;
    double distance;

    Vector2 normalB1ToB2 = (B2.MotionHandler.PositionGS - B1.MotionHandler.PositionGS).Normalized;

    foreach (Edge edge in B1.Edges)
    {
        if (edge.Normal.RelativePosition.Dot(normalB1ToB2) >= 0.0)
        {
            double minA, minB, maxA, maxB;
            B1.ProjectToAxis(edge.Normal.RelativePosition, B1.MotionHandler.MotionDataGet, out minA, out maxA);
            B2.ProjectToAxis(edge.Normal.RelativePosition, B2.MotionHandler.MotionDataGet, out minB, out maxB);


            if (minA < minB)
                distance = minB - maxA;
            else
                distance = minA - maxB;

            if (distance > 0.0f)
                return false;
            else if (Math.Abs(distance) < lowestDistance)
            {
                lowestDistance = Math.Abs(distance);

                collisionInfo.Normal = edge.Normal.RelativePosition;
                collisionInfo.Edge = edge;
            }
        }
    }


    Vector2 normalB2ToB1 = -normalB1ToB2;


    foreach (Edge edge in B2.Edges)
    {
        if (edge.Normal.RelativePosition.Dot(normalB2ToB1) >= 0.0)
        {
            double minA, minB, maxA, maxB;
            B1.ProjectToAxis(edge.Normal.RelativePosition, B1.MotionHandler.MotionDataGet, out minA, out maxA);
            B2.ProjectToAxis(edge.Normal.RelativePosition, B2.MotionHandler.MotionDataGet, out minB, out maxB);

            if (minA < minB)
                distance = minB - maxA;
            else
                distance = minA - maxB;

            if (distance > 0.0f)
                return false;
            else if (Math.Abs(distance) < lowestDistance)
            {
                lowestDistance = Math.Abs(distance);

                collisionInfo.Normal = edge.Normal.RelativePosition;
                collisionInfo.Edge = edge;
            }
        }
    }


    collisionInfo.Depth = lowestDistance;


    /* Double lowHighSeconds = elapsedSeconds;
    Double highLowSeconds = 0.0;
    Double seconds;
    IMotionData md1;
    IMotionData md2;
    bool collision;
    do
    {
        md1 = B1.MotionHandler.MotionDataLastGet.Copy;
        md2 = B2.MotionHandler.MotionDataLastGet.Copy;

        collision = true;
        lowestDistance = Double.MaxValue;
        seconds = MathExtensions.MathExt.Lerp(highLowSeconds, lowHighSeconds, 0.5);

        B1.MotionHandler.Simulate(seconds, ref md1);
        B2.MotionHandler.Simulate(seconds, ref md2);


        normalB1ToB2 = (md2.PositionGS - md1.PositionGS).Normalized;

        foreach (Edge edge in B1.Edges)
        {
            if ((edge.Normal.Position * B1.Model.Scale).Rotate(md1.RotationGS).Dot(normalB1ToB2) >= 0.0)
            {
                double minA, minB, maxA, maxB;
                B1.ProjectToAxis((edge.Normal.Position * B1.Model.Scale).Rotate(md1.RotationGS), md1, out minA, out maxA);
                B2.ProjectToAxis((edge.Normal.Position * B1.Model.Scale).Rotate(md1.RotationGS), md2, out minB, out maxB);


                if (minA < minB)
                    distance = minB - maxA;
                else
                    distance = minA - maxB;

                if (distance > 0.0f)
                    collision = false;
                else if (Math.Abs(distance) < lowestDistance)
                {
                    lowestDistance = Math.Abs(distance);

                    collisionInfo.Normal = (edge.Normal.Position * B1.Model.Scale).Rotate(md1.RotationGS);
                    collisionInfo.Edge = edge;
                }
            }
        }


        normalB2ToB1 = -normalB1ToB2;


        foreach (Edge edge in B2.Edges)
        {
            if ((edge.Normal.Position * B2.Model.Scale).Rotate(md2.RotationGS).Dot(normalB2ToB1) >= 0.0)
            {
                double minA, minB, maxA, maxB;
                B2.ProjectToAxis((edge.Normal.Position * B2.Model.Scale).Rotate(md2.RotationGS), md2, out minA, out maxA);
                B1.ProjectToAxis((edge.Normal.Position * B2.Model.Scale).Rotate(md2.RotationGS), md1, out minB, out maxB);


                if (minA < minB)
                    distance = minB - maxA;
                else
                    distance = minA - maxB;

                if (distance > 0.0f)
                    collision = false;
                else if (Math.Abs(distance) < lowestDistance)
                {
                    lowestDistance = Math.Abs(distance);

                    collisionInfo.Normal = (edge.Normal.Position * B2.Model.Scale).Rotate(md2.RotationGS);
                    collisionInfo.Edge = edge;
                }
            }
        }

        collisionInfo.Depth = lowestDistance;

        if (!collision)
        {
            lowHighSeconds = seconds;
        }
        else
        {
            highLowSeconds = seconds;
        }
    } while (Math.Abs(highLowSeconds - lowHighSeconds) > 0.0001);

    B1.MotionHandler.MotionDataSet = md1;
    B2.MotionHandler.MotionDataSet = md2; */

    // bool flip = false;
    if (collisionInfo.Edge.Parent != B2.Model)
    {
        Body temp = B1;
        B1 = B2;
        B2 = temp;
    }


    //This is needed to make sure that the collision normal is pointing at B1
    int Sign = Math.Sign(
        collisionInfo.Normal.Dot(
            B1.MotionHandler.MotionDataGet.PositionGS + (B1.Center * B1.Model.Scale).Rotate(B1.MotionHandler.MotionDataGet.RotationGS) -
            B2.MotionHandler.MotionDataGet.PositionGS + (B2.Center * B2.Model.Scale).Rotate(B2.MotionHandler.MotionDataGet.RotationGS)
        )
    );

    //Remember that the line equation is N*( R - R0 ). We choose B2->Center 
    //as R0; the normal N is given by the collision normal

    if (Sign != 1)
        collisionInfo.Normal = -collisionInfo.Normal; //Revert the collision normal if it points away from B1


    double SmallestD = double.MaxValue; //Initialize the smallest distance to a high value
        //Measure the distance of the vertex from the line using the line equation
    for (int t = 0; t < B1.Vertices.Count(); ++t)
    {
        double Distance = collisionInfo.Normal.Dot(B1.Vertices[t].WorldPosition - B2.Center);

        // If the measured distance is smaller than the smallest distance reported 
        // so far, set the smallest distance and the collision vertex
        if (Distance < SmallestD)
        {
            SmallestD = Distance;
            collisionInfo.Vertex = B1.Vertices[t];
        }
    }


    if ((Body.CollisionType & CollisionType.Velocity) > 0)
    {
        Vector2 vab1 = B1.MotionHandler.VelocityGS - B2.MotionHandler.VelocityGS;

        Vector2 rap = (B1.MotionHandler.PositionGS - collisionInfo.Normal);
        Vector2 rbp = (B2.MotionHandler.PositionGS - collisionInfo.Normal);

        Double rap2 = (rap.Cross(collisionInfo.Normal));
        Double rbp2 = (rbp.Cross(collisionInfo.Normal));

        Vector2 one = (collisionInfo.Vertex.WorldPosition - B1.MotionHandler.PositionGS).GetPerpendicular;
        Vector2 two = (collisionInfo.Vertex.WorldPosition - B2.MotionHandler.PositionGS).GetPerpendicular;

        Double j = (-(1 + 0.0) * vab1.Dot(collisionInfo.Normal)) /
            ((collisionInfo.Normal.Dot(collisionInfo.Normal) * (B1.MotionHandler.InverseMassGS + B2.MotionHandler.InverseMassGS)) +
            (one.Dot(one) * B1.MotionHandler.InverseInertiaGS) + (two.Dot(two) * B2.MotionHandler.InverseInertiaGS));


        B1.MotionHandler.AddImpulse = new Force(
            collisionInfo.Normal,
            j /* ,
            one */
        );
        B2.MotionHandler.AddImpulse = new Force(
            collisionInfo.Normal,
            -(j) /* ,
            two */
        );


        NewtonianMotionData data1 = (NewtonianMotionData)B1.MotionHandler.MotionDataGet;
        NewtonianMotionData data2 = (NewtonianMotionData)B2.MotionHandler.MotionDataGet;

        data1.AngularVelocity += (one.Dot(j * collisionInfo.Normal)) * data1.inverseInertia;
        data2.AngularVelocity += (two.Dot(-j * collisionInfo.Normal)) * data2.inverseInertia;

        B1.MotionHandler.MotionDataSet = data1;
        B2.MotionHandler.MotionDataSet = data2;
    }

    return true;
}
4

2 に答える 2

7

2つの問題があります。

1)コードに問題があります。あなたはそれを修正する必要があります。

2)あなたは「何か」が何であるかを理解する方法を知りません。

最初の問題を解決することは、2番目の問題を解決することにかかっています。作成したばかりのプログラムをデバッグする方法を学ぶ必要があります。

あなたはすでにそれをテストし、無意味であると特定した結果を得ました。それは良い第一歩です。今、それをさらに分解します。この領域で、鉛筆と紙で自分で解決できる簡単な問題を選んでください。そうしてから、アルゴリズムがデバッガーで同じ問題を解決するのを見て、途中のすべてのステップをチェックします。静かなしつこい疑問に耳を傾けます。 少し外れたり予期しないものがあった場合は、実行を停止し、問題が正しく機能しているかどうかを理解するまで問題を調査します。最終的には、物事が本来あるべき姿ではないステップが見つかり、そこにバグがあります。

はい、これは面倒です。バグを見つけて修正したら、一時停止して、最初にバグを書いた原因を考え、その種のバグを二度と書かないようにする方法を見つけます。

アップデート:

再:あなたの最近のコメント。

謝罪は受け入れられた。今落ち着いてください。これがうまくいったら、このバグを見つけることは決してないでしょう。あなたの脳はあなたを許しません。パニックに陥った、精力的な状態にある人間は、推論する能力を失います。そのため、防火扉は外側に開きます。燃えている建物から逃げる人間は、文字通り「私はこのドアを押しているのに開いていないので、引っ張ってみるべきだ」と考えるのをやめません。彼らはただ強く押すだけです。あなたはもっと強く押しているのではないかと思います。

デバッグには、合理性細部への細心の注意が必要です。あなたがすべてこの問題に取り組んでいるなら、それは窓の外に出て、それはただ悪化するでしょう。そこにいた人から取ってください。私たちは皆そこにいました。自分のプログラムにバグを引き起こし、それを見つけることができないのは、非常に苛立たしいことです。

誰もあなたを助けていない理由は...まあ、私があなたのデバッグ努力に集中する方法の漠然とした礼儀と提案以上のものであなたを助けるために満たされなければならない一連の前提条件をリストしましょう:

1)3D物理のシミュレーションについて何か知っている必要があります。私は1992年にニュートン力学の単純な微分方程式をかなりよく理解していましたが、それ以来それを使用していません。また、減衰駆動ばねの方程式は、剛体衝突の方程式とはかなり異なります。数週間かけてメモを確認すれば、数学を取り戻すことができますが、それは現実的ではありません。現在、3D衝突物理シミュレーションに精通している人が必要です。

2)私がよく知らない問題を解決するには、私以外の誰かが書いた数百行のコードであるあなたのコードを読んで理解できなければなりません。さらに悪いことに、そのコードの100行がコメント化されています。なんで?それは関係がありますか?そこにバグはありますか?さらに、デバッガーで実行せずにコードを読み取って理解できる必要があります。一体、私はそのコードをコンパイルすることさえできません。それは私が持っていない図書館に依存します。

さらに悪いことに、これらのライブラリの1つにバグが含まれている可能性があります。私が知っている限りでは、バグは、あなたが私たちに見せていないどこかで法線を計算するいくつかのコードのタイプミスです。示されているコードは完璧である可能性があります。

3)他の誰かの難しい問題に取り組むための自由な時間が必要です。コードを書き、物理学を理解している人が前進していないという問題。

これらはすべて要件です。それらのいずれかが欠落している場合、読者は効果的にあなたを助けることができません。あなたは、懐中電灯のない真夜中に暗い倉庫で黒猫を見つけるのを手伝ってくれることを知らない人に頼んでいます-そこにさえいないかもしれない猫。テイカーが少ないのも当然です。あなたの質問を読んだ74人のスタックオーバーフローユーザーのうち、3つの要件すべてを満たす人は何人ですか?私はそれらのどれにも会いません。

このサイトでヘルプが必要な場合は、より簡単な問題を投稿してください。問題を、物理学とシミュレーションアルゴリズムの特別な知識をあまり必要とせず、関連するコード、できればコンパイルして実行できるコードのみを持つ問題に絞り込みます。

于 2010-02-08T15:58:05.587 に答える
2

これは良いニュースではないかもしれませんが、EricLippertの分析に追加することがいくつかあります。

あなたのコメントは誤解を招くものです。数学と物理学に精通していない場合、正確にするのは難しいことを私は知っていますが、「ProjectToAxis」を見てください。

///軸に「垂直」な抽象的な1Dラインを投影します。  
///モデルの幅全体にストレッチ、
///その軸から測定。

これが厳しく聞こえるなら許してください、しかし

  • 「abstract1dline」は意味がなく、「line」とだけ言うべきです。
  • それは実際には線を投影していません。
  • 軸に垂直ではなく、軸に 平行な範囲を測定します。
  • それは「幅を超えて」ではなく、正確には、それはまさに最大の範囲です。
  • 「その軸から測った」というのは無意味か間違っているのか、どちらかわかりません。

私を信じてください、私はニトを選ぶつもりはありません、それは私がこのコードが何をすることになっているのかを理解しようとしているだけです、そして悪いコメントは何もないより悪いです。この関数が何をするかはわかりますが(「ドット」のような関数が宣伝どおりに機能すると仮定して)、それがあなたの望むことをするかどうかはまだわかりません

次に、DetectCollision(衝突を検出するだけではありません)を見てみましょう。

///各エッジでも2本の架空の線を投影します。
///見ている間各オブジェクトの幅に等しい
///そのエッジ、次にそれらが交差するかどうかを確認します。

何?私にできることは、これを無視してコードを確認することだけです...あまり意味をなさない部分があります(たとえば、なぜボディをそのエッジのすべてに投影するのですか?)、リバースエンジニアリング非常に難しいでしょう。

あなたが試しているアルゴリズムを知っていれば、バグを見つけることができます。コードが機能する場合は、アルゴリズムを推測することができます。しかし、コードが機能せず、(私が思うに)アルゴリズムを自分で本当に知らない場合、私たちはちょっと立ち往生しています。

うまくいく可能性のあるアプローチは次のとおりです。この関数は長すぎ、多くのことを実行し、どの部分が正しく実行されるかわかりません。エルゴあなたはそれをいくつかの機能に分解し、それらを個別にテストする必要があります。(Eric Lippertが説明した理由により、自分でそれを行うことはできません。)2つの関数に分割することから始めることができます。1つはCollisionInfoを計算し(ボディを一定のままにします)、もう1つはボディの動きを調整します(CollisionInfoを一定のままにします)。 )。

于 2010-02-09T20:40:42.660 に答える