Войти
ПрограммированиеСтатьиФизика

Двумерный движок физики многоугольников и окружностей на основе метода импульсов. (3 стр)

Автор:

Часть 5. Удары и импульсы

Импульсный метод подразумевает, что при взаимодействии тел (то есть при ударе) на тела действует одинаковый импульс, но с разным знаком по нормали контакта удара. Проще показать, как это выглядит математически (antiM = 1/M, antiI = 1/I, (mass_x, mass_y) — координаты центра масс тела, (x, y) — точка, к которой прикладывается импульс impulse по нормали normal; movement — это объект, который управляет движением тела):

void BODY::apply_impulse(double x, double y, DOT2D normal, double impulse)
{
  DOT2D RA(x - mass_x, y - mass_y);
  movement->V.x += impulse * normal.x * movement->antiM;
  movement->V.y += impulse * normal.y * movement->antiM;
  movement->W += impulse * (normal.y * RA.x - normal.x * RA.y) * movement->antiI;
}

Указанный выше метод прикладывает импульс к телу. Внимательно изучите этот код. Это основа метода импульсов. Расшифровать его можно примерно так: импульс действует на тело как сила, но только уже помноженная на некий временной интервал. Импульс изменяет скорость тела, тело сопротивляется массой M. И импульс закручивает тело — это самая хитрая часть. Давайте разберемся с последней строкой этого метода. В ней показано, что на тело действует закручивающая сила, тело сопротивляется моментом инерции и есть еще некий множитель:

(normal.y * RA.x - normal.x * RA.y)

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

Очень важно. Импульс — всегда положителен или равен 0. Если импульс больше 0, то тела разлетаются по контакту. Если вы примените отрицательный импульс, возникнет эффект «притяжения» тел по контакту. Может у вас такое и будет возможно (для гравитационной пушки как в HL2 :)?..

Что происходит при ударе двух тел? На первое тело (точнее на то, на которое направлена нормаль контакта) действует импульс impulse, а на второе (то есть на тело, которому принадлежит нормаль контакта) действует импульс – impulse:

 
body_1->apply_impulse(x,y,normal, impulse);
body_2->apply_impulse(x,y,normal, -impulse);

Еще раз: нормаль направлена к телу 1 от тела 2. Вот собственно и все :-). Шучу. Мы уже знаем, как применить произвольный импульс к произвольной точке тела. (Кстати, точка эта не обязана принадлежать телу.) С помощью этого метода уже можно написать код, который «таскает тела». Осталась мелочь: как же вычислить значение импульса impulse произвольного удара между телами? Введем еще одну величину: E. Это изменение относительной скорости точек контакта тел (по нормали). То есть:

Vab_после_удара = –E * Vab_до удара, 

Vab — относительная скорость точек контакта тел по нормали (не центров масс! А именно тех краевых точек тел, по котором происходит контакт.).

Рассмотрим отдельные значения E. Если E = 0, то точки контакта тел после удара двигаются одинаково. Это выглядит так — тела слипаются и движутся далее вместе (по нормали контакта, разумеется!). Если E = 1, то точки контакта тел после удара разлетаются с такой скоростью, с какой слетались. Это пример упругого соударения — так соударяются, скажем, бильярдные шары (ну, обычно ;) ). Если E = 0.5, то это то, что нам нужно — тела разлетаются примерно так, как если бы были сделаны из пластика.
Случай E = –1 будем рассматривать? :) Он означает, что относительная скорость точек контакта не изменилась, то есть тела просто пролетают друг через друга. Имеет также смысл рассмотреть случай E = –0.5: относительная скорость пролета тел «сквозь» контакт уменьшается вдвое.

Контакт это тот же самый удар, только с E = 0.0 (или с –0.5 — попробуйте, даже лучше «устаканивается»)

Пусть P — точка контакта, R1 и R2 — вектора из центра масс тела 1 и 2 (нормаль направлена от 2 к 1) в точку контакта (mass_x, mass_y — центр масс).

R1.x = P.x - body1->mass_x;
R1.y = P.y - body1->mass_y;

Относительная скорость точек тел по контакту (m1 — объект движения тела 1, m2 — объект движения тела 2)

double Vab()
{
    return
      normal.x * (m1->V.x - m1->W * R1.y - m2->V.x + m2->W * R2.y) + 
      normal.y * (m1->V.y + m1->W * R1.x - m2->V.y - m2->W * R2.x);
}

Плечи контакта — Z1, Z2:

Z1 = (normal.y * R1.x - normal.x * R1.y) * m1->antiI;

Вспомогательная величина:

J = normal.x *  (normal.x * m1->antiM - R1.y * Z1 + normal.x * m2->antiM + R2.y * Z2) 
+ normal.y * (normal.y * m1->antiM + R1.x * Z1 * m1->antiI
+ normal.y * m2->antiM - R2.x * Z2 * m2->antiI);

Итак, мы готовы выписать формулу расчета импульса:

impulse = ( MIN_V - (1 + E) * Vab() ) / J;

Что такое MIN_V — см. ниже, пока считаем его равным нулю.

Повторюсь еще раз. Пусть мы вычислили импульс, а он меньше нуля. Что же делать? Да ничего! Нет контакта или удара, а притягиваться телам — запрещено законом Ома! ;-)

Часть 6. Как это все работает.

Работает это все примерно так: движок вызывают и передают ему период времени dt, в течение которого движок ничего не делал. Шаг dt подбирается с помощью бубна и медитаций. Если он будет слишком большой — забудьте об «устаканивании», тела будут все время подрагивать. Если слишком малый — забудьте о производительности. Если прошедший интервал времени больше dt, что надо сделать? Правильно вызвать движок несколько раз с шагом <= dt.

В движке за время dt делаем:

— вычисляем силы, действующие на тела в течение dt и применяем их
— решаем контакты и трение (см. далее)
— двигаем тела, ищем пересечения между ними, пересечения - это удары.

Итак, что такое «решаем контакты»?

Что такое контакт? Это всего лишь указание, какие фигуры каких тел находятся в контакте. Точка, нормаль и расстояние взаимопроникновения по контакту определяются каждый раз после передвижения тела. Тут же решаем — не разошлись ли тела слишком далеко и отбрасываем такие контакты. То есть контакты отбрасываются не сразу после того, как тела начали по ним разлетаться! Ни-ни! Иначе у нас объем вычислений просто чудовищным станет (в части поиска пересечений разумеется).

Откуда берутся контакты? Ими становятся удары, уже после применения удара (то есть после применения к телам импульса удара). Если между фигурами есть контакт, то пересечения их нам искать не нужно — удара по ним никогда не будет.

Вводим эмпирически некую минимальную скорость разлета тел, которая зависит от величины их взаимопроникновения (это и есть MIN_V).

Теперь нам надо «ударять» тела по контактам, пока удары все еще будут происходить (импульсы будут положительны).

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

Вот, собственно, и все.

Для замечаний и отзывов — или в комментариях к статье.

Страницы: 1 2 3

#2D, #импульсы

23 апреля 2005 (Обновление: 9 янв. 2012)

Комментарии [242]