tgoop.com/cxx95/50
Last Update:
#library
Классическая механика на C++ - обзор движка Box2D 🚀
Box2D это физический движок, используется в основном в играх. Игры с Box2D можно посмотреть на YouTube. Самой популярной игрой является, наверное, Angry Birds.
Box2D рассчитывает физику абсолютно твердых тел, то есть в нем нельзя эмулировать движение жидкостей или делать игры наподобии Worms.
✏️ "Фигура" в Box2D это круг, многоугольник (не больше 8 углов), или отрезок.
✏️ "Тело" состоит из 1+ фигур, можно придать свою плотность, коэффициент трения и упругость.
✏️ Телу можно придавать "ограничения", например запрещать вращение или движение по оси X/Y.
✏️ Между телами могут быть "связи", которые будут держать тела вместе, разных типов.
✏️ У "связей" также могут быть разные ограничения, например в эмуляции человеческого локтя ограничен возможный угол между частями руки.
✏️ "Мир" содержит в себе эти объекты и управляет памятью и эмуляцией движения.
✏️ В любой момент можно добавлять/удалять тела, применять силу, вращение, импульс... Также можно проверять коллизии тел, делать raycast, вычислять расстояние между телами и многое другое.
Эти понятия можно комбинировать в самые сложные конфигурации - пример транспорта на YouTube.
Константа time step
определяет, сколько времени "прошло" с предыдущего перерасчета мира. Обычно мир пересчитывают 60 раз в секунду (time step = 1.0f / 60.0f
).
В алгоритмах используется простая математика. Из старших классов школы - вычисление нормалей, матрицы вращения, произведение векторов, тригонометрия. Из первого курса вуза - метод Эйлера.
Box2D можно сбилдить всего за 5 секунд, даже вместе с тестовым приложением.
Он написан на суржике C и C++: не используются namespace, константы через enum, есть самодельные списки объектов и т.д.
Например, если объект должен находиться в списке, то указатель на следующий объект списка находится прямо в классе, а не "снаружи":
for (b2Body* b = myWorld->GetBodyList(); b; b = b->GetNext())В Box2D используется реализация двух популярных типов аллокаторов:
b->... // do something with the body
💾 Аллокатор маленьких объектов (b2BlockAllocator) - вместо того, чтобы постоянно вызывать
malloc
/free
для маленьких объектов, этот аллокатор сразу запрашивает 16kb памяти и создает объекты там (пока есть место).💾 Аллокатор на стеке (b2StackAllocator) - на стеке лежит 100kb памяти, объекты создаются там.
Это позволяет во время "перерасчета мира" обходиться без аллокаций памяти.
Все объекты нужно создавать через "мир" (b2World):
b2Body* b = myWorld->CreateBody(&bodyDef);
Удалять тоже нужно через "мир", хотя деструктор b2World::~b2World()
очистит всё что не удалено вручную.⏱ Движок использует разные техники, чтобы быстрее делать перерасчет мира.
Если перемещение (угол/импульс/...) объектов считается быстро, то для быстрого расчета коллизий нужно использовать структуры данных.
Можно подробно почитать об этой проблеме на wikipedia. Целью оптимизаций является получение алгоритма за
O(N)
вместо O(N^2)
, где N
-количество объектов.Box2D использует "динамическое дерево" (b2DynamicTree) - весь мир делится на две части, каждая часть тоже делится на две, и так далее.
🐷 Можно на примере свинок из Angry Birds придумать игровую логику.
Если вы играли в Angry Birds, то помните, что свинки довольно хрупкие, но они могут остаться в живых (потеряв "здоровье"), если упадут с небольшой высоты, или на них упадет некрупная балка.
Мерой урона для свинки может считаться импульс коллизии с другим предметом.
strength = M_свинка * V_свинка + M_предмет * V_предметМожно итерироваться по всем "контактам" между телами, чтобы поймать начало коллизии:
for (b2Contact* contact = world->GetContactList(); contact; contact = contact->GetNext())Но это неэффективно и некрасиво. Лучше использовать коллбек на коллизию через b2ContactListener
contact->... //do something with the contact
BY C++95
Share with your friend now:
tgoop.com/cxx95/50