Я пытаюсь заставить базовую динамику столкновения работать

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

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

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

/// <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 ответа

У тебя две проблемы.

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

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

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

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

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

ОБНОВИТЬ:

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

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

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

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

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

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

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

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

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

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

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

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

/// Проецирует абстрактную 1D линию "перпендикулярно" оси,  
/// растягивается по ширине модели, 
/// измерено от этой оси. 

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

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

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

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

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

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

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

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

Другие вопросы по тегам