Жизненный цикл симуляции

В Ammo.js создание физического мира начинается с определения конфигурации и компонентов, управляющих симуляцией. Основными элементами являются:

  • btDefaultCollisionConfiguration — задаёт базовые параметры коллизий и выделение памяти под алгоритмы столкновений.
  • btCollisionDispatcher — отвечает за обработку столкновений между объектами.
  • btDbvtBroadphase — структура для ускоренной проверки потенциальных столкновений.
  • btSequentialImpulseConstraintSolver — решатель физических ограничений, обеспечивающий корректную симуляцию сил и импульсов.

Создание физического мира выполняется через:

const collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
const dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
const broadphase = new Ammo.btDbvtBroadphase();
const solver = new Ammo.btSequentialImpulseConstraintSolver();
const physicsWorld = new Ammo.btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);
physicsWorld.setGravity(new Ammo.btVector3(0, -9.81, 0));

После инициализации мир готов к добавлению объектов и проведению симуляции.


Создание и добавление физических объектов

Физические объекты делятся на несколько типов:

  1. Rigid Body (жёсткое тело) — основной тип объектов с массой, столкновениями и динамическим поведением.
  2. Static Body (статическое тело) — объекты с бесконечной массой, которые не подвержены силам и движению.
  3. Kinematic Body (кинематическое тело) — управляется вручную, но взаимодействует с динамическими объектами через коллизии.

Процесс создания rigid body состоит из нескольких шагов:

  1. Создание формы столкновения (collision shape), например, btBoxShape, btSphereShape или btCapsuleShape.
  2. Вычисление локального инерционного тензора через calculateLocalInertia(mass).
  3. Формирование motion state, описывающего позицию и ориентацию тела:
const transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(x, y, z));
const motionState = new Ammo.btDefaultMotionState(transform);
  1. Создание rigid body с использованием btRigidBodyConstructionInfo:
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, shape, localInertia);
const body = new Ammo.btRigidBody(rbInfo);
physicsWorld.addRigidBody(body);

Ключевым моментом является управление памятью Ammo.js: объекты типа btVector3, btTransform требуют явного освобождения через Ammo.destroy() после использования, иначе возникает утечка памяти.


Шаг симуляции

Симуляция физики выполняется через метод stepSimulation(deltaTime, maxSubSteps, fixedTimeStep). Параметры:

  • deltaTime — время, прошедшее с предыдущего кадра (в секундах).
  • maxSubSteps — максимальное количество внутренних шагов симуляции для компенсации больших кадров.
  • fixedTimeStep — фиксированный интервал времени для каждого внутреннего шага.

Пример:

physicsWorld.stepSimulation(1/60, 10, 1/60);

Система автоматически обновляет позиции и ориентации всех добавленных тел. Для получения текущего состояния rigid body используется метод motion state:

const ms = body.getMotionState();
if (ms) {
    const transform = new Ammo.btTransform();
    ms.getWorldTransform(transform);
    const origin = transform.getOrigin();
    const rotation = transform.getRotation();
}

Управление взаимодействиями и коллизиями

Ammo.js поддерживает расширенные возможности взаимодействия:

  • Коллизионные группы и маски: позволяют исключить определённые типы объектов из проверки столкновений.
physicsWorld.addRigidBody(body, group, mask);
  • Контактные точки: доступ через getDispatcher().getManifoldByIndexInternal(i), позволяя считывать силу столкновения, нормали и позиции контакта.
  • Применение внешних сил и импульсов:
body.applyCentralForce(new Ammo.btVector3(fx, fy, fz));
body.applyImpulse(new Ammo.btVector3(ix, iy, iz), new Ammo.btVector3(px, py, pz));

Обновление и синхронизация с визуализацией

Позиции объектов в физическом мире необходимо синхронизировать с графикой (Three.js, Babylon.js или Canvas). После stepSimulation можно извлечь трансформации:

const transform = new Ammo.btTransform();
body.getMotionState().getWorldTransform(transform);
mesh.position.set(transform.getOrigin().x(), transform.getOrigin().y(), transform.getOrigin().z());
mesh.quaternion.set(transform.getRotation().x(), transform.getRotation().y(), transform.getRotation().z(), transform.getRotation().w());

Для оптимизации производительности рекомендуется повторно использовать объекты btTransform и btVector3, а не создавать их каждый кадр.


Очистка и завершение симуляции

Ammo.js не использует сборщик мусора JavaScript для своих объектов C++. После завершения симуляции необходимо:

  1. Удалить тела из мира: physicsWorld.removeRigidBody(body).
  2. Освободить память объектов: Ammo.destroy(body), Ammo.destroy(shape), Ammo.destroy(transform) и т.д.
  3. Освободить сам мир и его компоненты:
Ammo.destroy(physicsWorld);
Ammo.destroy(solver);
Ammo.destroy(broadphase);
Ammo.destroy(dispatcher);
Ammo.destroy(collisionConfiguration);

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


Особенности жизненного цикла объектов

  • Создание → Добавление в мир → Симуляция → Извлечение состояния → Удаление → Освобождение памяти.
  • Объекты, не добавленные в мир, не участвуют в расчётах коллизий и сил.
  • События столкновений можно обрабатывать через обход dispatcher после каждого шага симуляции.
  • Масштаб времени и силы тяжести можно менять динамически, что влияет на всю систему без пересоздания объектов.

Жизненный цикл симуляции в Ammo.js требует внимательного управления объектами и памяти, что обеспечивает реалистичную физику и стабильную работу приложения в JavaScript.