Движок физики 2D с упругостью полигонов с нарушениями

Вопрос или проблема

Я пытался создать простой двухмерный физический движок на C#. Следовал уроку от “Two-Bit Coding” на YouTube, но заметил, что квадраты с двумя контактными точками вообще не отскакивают. Причину этой проблемы я выяснил: импульсы были просто слишком маленькими при разрешении столкновений.

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

Я попробовал другую формулу для разрешения столкновения, на этот раз из Википедии. Формула предназначена для 3D, но я предположил, что она будет работать и для 2D.
Формула из Википедии

Вот реализация на C#:

        public void Resolve_collision_wiki(in Physics_manifold contact)
        {
            Physics_body body_a = contact.body_a;
            Physics_body body_b = contact.body_b;
            Physics_vector normal = contact.normal;

            int contact_count = contact.contact_count;

            float e = 1;

            contact_list[0] = contact.contact_point_1;
            contact_list[1] = contact.contact_point_2;

            for (int i = 0; i < contact_count; i++)
            {
                Physics_vector ra = contact_list[i] - body_a.Position;
                Physics_vector rb = contact_list[i] - body_b.Position;

                center_a_contact_list[i] = ra;
                center_b_contact_list[i] = rb;

                Physics_vector relative_velocity = body_b.Linear_velocity - body_a.Linear_velocity + Physics_math.Cross_product(body_b.Angular_velocity, rb) - Physics_math.Cross_product(body_a.Angular_velocity, ra);

                //() сначала, затем скалярное произведение?
                float numerator = Physics_math.Dot_product(-(1 + e) * relative_velocity, normal);

                if (numerator < 0)
                    return;

                //https://www.cs.cmu.edu/~baraff/sigcourse/notesd2.pdf#page=16

                numerator /= body_a.inverse_mass + body_b.inverse_mass + Physics_math.Dot_product(Physics_math.Cross_product(body_a.inverse_inertia * Physics_math.Cross_product(ra, normal), ra) + Physics_math.Cross_product(body_b.inverse_inertia * Physics_math.Cross_product(rb, normal), rb), normal);
                numerator = MathF.Max(numerator, 0);

                j_list[i] = numerator;
            }

            //Применение в первом цикле сделает второй импульс немного неправильным
            for (int i = 0; i < contact_count; i++)
            {
                body_a.Linear_velocity -= j_list[i] * body_a.inverse_mass * normal;
                body_a.Angular_velocity -=  Physics_math.Cross_product(center_a_contact_list[i], normal * j_list[i]) * body_a.inverse_inertia;

                body_b.Linear_velocity += j_list[i] * body_b.inverse_mass * normal;
                body_b.Angular_velocity +=  Physics_math.Cross_product(center_b_contact_list[i], normal * j_list[i]) * body_b.inverse_inertia;
            }
        }

Реализация скалярного произведения и двумерного векторного произведения:

        public static float Dot_product(Physics_vector a, Physics_vector b)
        {
            return a.x * b.x + a.y * b.y;
        }

        public static float Cross_product(Physics_vector a, Physics_vector b)
        {
            return a.x * b.y - a.y * b.x;
        }

        public static Physics_vector Cross_product(float a, Physics_vector v)
        {
            return new Physics_vector(-a * v.y, a * v.x);
        }

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

Ответ или решение

Проблема с восстановлением импульса в 2D-движке физики для полигонов

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

Понимание проблемы

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

Использование формулы из Википедии

Формула, которую вы используете из Википедии, вероятно, не учитывает определённые 2D-особенности. Главная ошибка заключается в том, что вы неверно применяете эту формулу, которая изначально создана для 3D. Иногда такие упрощения могут привести к нежелательным результатам в 2D. Вы можете рассмотреть модификацию подхода, специализированного для 2D.

Рекомендации по корректировке кода

  1. Коррекция расчётов момента импульса: Убедитесь, что вы правильно учитываете два момента (относительно каждого тела) при расчёте. Попробуйте использовать следующее изменение в основном методе Resolve_collision_wiki:

    float I_a = body_a.inverse_inertia;
    float I_b = body_b.inverse_inertia;
    
    float denominator = body_a.inverse_mass + body_b.inverse_mass +
       (I_a * Physics_math.Dot_product(ra, ra) + I_b * Physics_math.Dot_product(rb, rb)) * Physics_math.Dot_product(normal, normal);

    Это позволит более точно рассчитывать восстановление, когда объекты находятся в движении.

  2. Числовая стабильность: Проверьте, чтобы ваши значения инверсных масс и инверсных моментов не стали равными нулю. Используйте маленькие значения (например, 1e-6) для этих расчётов, чтобы избежать деления на ноль.

  3. Эластичность и коэффициент восстановления: Убедитесь, что коэффициент восстановления (e) правильно внедрён. Значение, равное единице, при идеальном столкновении для всех форм не всегда будет достигнуто. Вы можете добавить переключатель для различных форм и сделать различные типы хотя бы для кругов и квадратов.

Проблема с изменением высоты отскока

Чтобы избежать проблемы с изменением высоты отскока при смещении точек контакта, рассмотрите использование различных подходов к реализации законов сохранения. Возможно, вам стоит перейти к,системе "Impulse Resolution", которая рассчитывает отскок на основе не только абсолютных значений, но и относительных позиций объектов.

Заключение

Ваша цель заключается в создании стабильного и надежного 2D-движка физики, и устранение проблем, связанных с восстановлением импульсов при столкновении многоугольников, имеет критическое значение. Проверив свои расчёты импульса и обеспечив их достаточной стабильностью и точностью, вы сможете добиться желаемых результатов.

Используйте эти рекомендации для улучшения вашего кода и достижения более предсказуемого поведения в столкновениях. Удачи в реализации!

Оцените материал
Добавить комментарий

Капча загружается...