Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Рэгдолл физика своими руками. Часть первая

Anna | 25.06.2014 | нет комментариев

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


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


Объединив 2 уравнения, мы получим


Уравнение Верле немного чем отличается от записи выше.


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


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


Если облечь всё это в код на С , то получится что-то такое

struct Point
{
  Vec2 Position;
  Vec2 OldPosition;
  Vec2 Acceleration;
};

class Physics 
{
  int PointCount;
  Point* Points[ MAX_VERTICES ];
  float Timestep;
public:
  void  UpdateVerlet();
};

void Physics::UpdateVerlet() 
{
  for( int I = 0; I < PointCount; I   ) 
 {
    Point& P = *Points[ I ];

    Vec2 Temp = P.Position;
    P.Position  = P.Position - P.OldPosition   P.Acceleration*Timestep*Timestep;
    P.OldPosition = Temp;
  }
}

Этого довольно для изложения движения физических точек. И это отлично, если у вас в игре планируются только точки. Если же хочется чего-то больше увлекательного, нужно делать твёрдые тела. В природе твёрдое тело это много точек-атомов, соединённых между собой необъяснимыми силами природы. Увы, для симуляции физики в игре сходственный способ подходит нехорошо, ни один компьютер не сумеет вынести расчётов миллиардов и миллиардов точек и связей между ними. Следственно мы обойдёмся моделированием вершин физических тел. Для квадрата таким образом будет довольно 4 точек и связей между ними. Осталось только описать эти связи. Видимо, что расстояние между вершинами должно оставаться непрерывным. Если расстояние между вершинами будет меняться, тело будет деформироваться, а мы этого не хотим (те, кто этого хотят, пока молчат). Как же сберечь расстояние между вершинами непрерывным? В действительности для решения этой задачи довольно было бы запихнуть между вершинами арматурину соответствующей длинны. Не мудрствуя коварно, поступим так же и в нашей симуляции. Сделаем класс, тот, что будет описывать эту воображаемую арматурину, удерживающую вершины на нужном расстоянии. Алгорифм, исполняющий эту работу, будет вызываться сразу же позже обработки движения точек-вершин. В сути своей он дюже примитивный. Мы расчитываем вектор между двумя вершинами, соединёнными нашей воображаемой арматуриной и сопоставляем длину этого вектора с нашей эталонной длинной. Если длины отличаются, нам остаётся только сдвинутьраздвинуть вершины на нужное расстояние, что бы длины вновь совпадали и вуаля – дело в шляпе.
Вот так это всё выглядит в коде:

struct Edge
{
  Vertex* V1;
  Vertex* V2;
  float OriginalLength; 
};

void Physics::UpdateEdges() 
{
  for( int I = 0; I < EdgeCount; I   ) 
 {
    Edge& E = *Edges[ I ];
    //Расчёт вектора между вершинами
    Vec2 V1V2 = E.V2->Position - E.V1->Position; 
    float V1V2Length = V1V2.Length(); 
    //Расчёт разницы в длине
    float Diff       = V1V2Length - E.OriginalLength; 
    V1V2.Normalize();
    //Корректировка расстояния
    E.V1->Position  = V1V2*Diff*0.5f; 
    E.V2->Position -= V1V2*Diff*0.5f;
  }
}

Вот так вот. Если сделать несколько вершин и объединить их между собой такими вот сторонами, результирующее тело будет показывать свойства твёрдого тела, в том числе вращение, вызываемое соударением с другими телами и полом. Как это всё работает? чай мы каждого-то добавили новое правило для сохранения расстояния между точками, а здесь на – сразу твёрдое тело. Секрет в том самом способе интегрирования Верле. Как мы помним, данный способ не оперирует скоростями, взамен этого в нём применяется разница между координатам нынешнего и ветхого расположения точки. Как следствие, от метаморфозы координаты изменится и скорость. А для сохранения непрерывной длинны между вершинами мы как раз и меняем их координаты. Итоговое метаморфоза скорости вершин даёт результат, приближённо напоминающий поведение твёрдых тел. Вернее примерно твёрдых. В том виде, в котором код находится теперь, тела будут деформироваться при ударах о пол и друг об друга. Отчего? Всё дюже легко. Если вершина присоединена больше чем к одной стороне, а так обыкновенно и будет протекать, корректировка длины одной из сторон механически приведёт к изменению длины иной стороны.


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

 

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB