Я пытаюсь заставить базовую динамику столкновения работать - PullRequest
4 голосов
/ 08 февраля 2010

Я упростил все до кубов / одного куба, сталкивающегося с прямоугольником бесконечной массы и следующего кода:

Проблема в том, что ящики имеют тенденцию слишком сильно вращаться и зацикливаться на вращении, и, если включен бинарный поиск, просто нажимать и крутить много.

Спасибо за любую помощь.

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

Ответы [ 2 ]

7 голосов
/ 08 февраля 2010

У вас две проблемы.

1) Что-то не так с кодом. Вам нужно это исправить.

2) Вы не знаете, как выяснить, что такое «что-то».

Решение первой проблемы зависит от решения второй проблемы. Вам нужно научиться отлаживать программу, которую вы только что написали.

Вы уже проверили это и получили результат, который вы определили как бессмысленный. Это хороший первый шаг. Теперь разбей его еще дальше. Выберите простую задачу в этой области, которую вы можете решить самостоятельно карандашом и бумагой; сделайте это, а затем посмотрите, как ваш алгоритм решает ту же проблему в отладчике, проверяя каждый шаг на этом пути. Слушайте тихие, ноющие сомнения. Когда есть что-то, что выглядит немного странно или неожиданно, остановите то, что вы делаете, и исследуйте проблему, пока не поймете, работают ли вещи правильно или нет. В конце концов вы найдете шаг, где все не так, как должно быть, и именно в этом и заключается ошибка.

Да, это утомительно. Когда вы нашли ошибку и исправили ее, сделайте паузу и подумайте о том, что заставило вас написать ошибку, и выясните, как больше не писать такую ​​ошибку.

UPDATE:

Re: ваши последние комментарии.

Извинение принято. Теперь успокойся . Вы никогда не найдете эту ошибку, если вы это исправили. Ваш мозг не позволит вам. Люди, находящиеся в паническом, возбужденном состоянии, теряют способность рассуждать. Вот почему противопожарные двери открываются наружу; люди, спасающиеся от горящего здания, буквально не перестанут думать: «Я толкаю эту дверь, и она не открывается, может, мне стоит попробовать потянуть». Они просто толкают сильнее. Я подозреваю, что вы давите сильнее.

Отладка требует рациональности и тщательного внимания к мелким деталям . Если вы все разбираетесь в этой проблеме, то это вылетит в окно, и будет только хуже. Возьми это у кого-то, кто был там. Мы все были там. Очень неприятно, что в вашей собственной программе возникла ошибка, которую вы не можете найти.

Причина, по которой вам никто не помогает, заключается в том, что ... ну, позвольте мне перечислить набор предварительных условий, которые должны быть выполнены для меня, чтобы помочь вам с более чем неопределенными банальностями, и предложения о том, как сосредоточить ваши усилия по отладке:

1) Мне нужно кое-что знать об симуляции трехмерной физики. В 1992 году я достаточно хорошо разбирался в дифференциальных уравнениях простой ньютоновской механики, но с тех пор не использовал его. И уравнение затухающей ведомой пружины несколько отличается от уравнений столкновений твердого тела. Если бы я потратил пару недель, просматривая свои записи, я мог бы вернуть математику, но это нереально. Вам нужен кто-то глубоко осведомленный прямо сейчас с 3d симуляциями физики столкновений.

2) Я должен уметь читать и понимать ваш код, код которого состоит из сотен строк, написанных кем-то кроме меня, чтобы решить проблему, с которой я не знаком. Хуже того, сотня строк этого кода закомментирована. Зачем? Это актуально? Есть ли ошибка там? Более того, мне нужно уметь читать и понимать код , не запуская его в отладчике . Черт, я даже не могу скомпилировать этот код. Это зависит от библиотек, которых у меня нет.

И, что еще хуже, одна из этих библиотек может содержать ошибку . Насколько я знаю, ошибка - это опечатка в каком-то коде, который вычисляет нормальное значение где-то, что вы нам не показали. Показанный код может быть идеальным.

3) Мне нужно свободное время, чтобы поработать над чужой трудной проблемой; проблема, которую человек, который написал код и понимает физику, не продвигается вперед.

Все это требования; если какой-либо из них отсутствует, читатель не сможет эффективно помочь вам. Вы просите людей, которых вы не знаете, помочь вам найти черного кота в темном складе в полночь без фонарика - кота, которого может даже не быть. Не удивительно, что у вас мало покупателей. Сколько из 74 пользователей переполнения стека, которые прочитали ваш вопрос, сколько из них отвечают всем трем требованиям? Я встречаю нет из них.

Если вам нужна помощь на этом сайте, тогда опубликуйте более легкую проблему . Сузьте проблему до проблемы, которая требует меньше специальных знаний по физике и алгоритмам моделирования и имеет только соответствующий код, предпочтительно код, который можно скомпилировать и запустить.

2 голосов
/ 09 февраля 2010

Это не может быть хорошей новостью, но у меня есть пара вещей, которые можно добавить к анализу Эрика Липперта, и предложение.

Ваши комментарии вводят в заблуждение. Я знаю, что если вы не знакомы с математикой и физикой, трудно быть точным, но взгляните на «ProjectToAxis»:

/// Projects an abstract 1D line "perpendicular" to the axis,  
/// stretching across the width of the model, 
/// measured from that axis. 

Простите, если это звучит грубо, но

  • «Абстрактная 1-я линия» бессмысленна, она должна просто сказать «линия».
  • Это не совсем проекция линии.
  • Он измеряет экстент параллельно оси, а не перпендикулярно ей.
  • Это не "по всей ширине", это просто самая большая степень.
  • «измерено по этой оси» - бессмысленно или неправильно, я не могу сказать, какой именно.

Поверьте, я не пытаюсь выбрать nits, просто я пытаюсь выяснить, что должен делать этот код, и плохой комментарий хуже, чем ничего. Я могу видеть, что делает эта функция (при условии, что такие функции, как «Dot» работают так, как объявлено), , но я до сих пор не знаю, выполняет ли она то, что вы хотите, чтобы она делала .

Теперь я взгляну на DetectCollision (который делает больше, чем просто обнаружение столкновения):

/// 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. 

Что? Все, что я могу сделать, это проигнорировать это и посмотреть на код ... Есть его части, которые не имеют особого смысла (например, почему, черт возьми, вы проецируете тело на каждый его ребер?), Поэтому обратное проектирование будет очень трудным.

Если бы я знал алгоритм, который вы пытались использовать, я мог бы попытаться найти ошибку. Если код работает, я мог бы попытаться вывести алгоритм. Но если код не работает и (как я подозреваю) вы сами не знаете алгоритм, мы застряли.

Вот подход, который может сработать : эта функция слишком длинная, она много делает, и вы не знаете, какие части она делает правильно. Поэтому вы должны разбить его на несколько функций и проверить их индивидуально. (Я не могу сделать это сам, по причинам, изложенным Эриком Липпертом.) Вы можете начать с разбивки на две функции: одну, которая вычисляет CollisionInfo (оставляя тела постоянными), и другую, которая регулирует движение тел (оставляя CollisionInfo постоянной). ).

...