Максимальное число подшагов

Ammo.js — это порт библиотеки Bullet Physics на JavaScript с использованием Emscripten, предоставляющий мощный движок физики для браузера. Он позволяет создавать реалистичные симуляции твердых тел, столкновений и динамики объектов с высокой производительностью, подходящей для игр и интерактивных веб-приложений.

Основной принцип работы Ammo.js — это разделение сцены на объекты физики, которые обрабатываются в отдельной системе симуляции. Каждый объект имеет тело (rigid body), физические свойства и форму (collision shape).


Инициализация физического мира

Создание физического мира начинается с конфигурации и выбора подходящего дискретного динамического мира:

// Настройка конфигурации коллизий
const collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
const dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
const broadphase = new Ammo.btDbvtBroadphase();
const solver = new Ammo.btSequentialImpulseConstraintSolver();

// Создание физического мира
const dynamicsWorld = new Ammo.btDiscreteDynamicsWorld(
    dispatcher,
    broadphase,
    solver,
    collisionConfiguration
);

// Установка гравитации
dynamicsWorld.setGravity(new Ammo.btVector3(0, -9.81, 0));

Ключевые моменты:

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

Создание тел и форм

Каждое тело физики состоит из формы (collision shape) и массы, а также имеет позицию и ориентацию. Наиболее часто используются следующие формы:

  • btBoxShape — куб/прямоугольник.
  • btSphereShape — сфера.
  • btCylinderShape — цилиндр.
  • btCapsuleShape — капсула.
  • btConvexHullShape — произвольная выпуклая форма.

Пример создания куба:

const boxShape = new Ammo.btBoxShape(new Ammo.btVector3(1, 1, 1));
const mass = 1;
const localInertia = new Ammo.btVector3(0, 0, 0);
boxShape.calculateLocalInertia(mass, localInertia);

const startTransform = new Ammo.btTransform();
startTransform.setIdentity();
startTransform.setOrigin(new Ammo.btVector3(0, 10, 0));

const motionState = new Ammo.btDefaultMotionState(startTransform);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, boxShape, localInertia);
const body = new Ammo.btRigidBody(rbInfo);

dynamicsWorld.addRigidBody(body);

Важные детали:

  • calculateLocalInertia необходим для подвижных объектов с ненулевой массой.
  • btDefaultMotionState управляет позицией и ориентацией тела между шагами симуляции.

Работа с динамикой и шаг симуляции

Для обновления состояния физического мира используется stepSimulation, принимающий время кадра и количество итераций:

function updatePhysics(deltaTime) {
    dynamicsWorld.stepSimulation(deltaTime, 10);

    // Обновление позиции объекта
    const transform = new Ammo.btTransform();
    body.getMotionState().getWorldTransform(transform);
    const origin = transform.getOrigin();
    console.log(`Позиция куба: x=${origin.x()}, y=${origin.y()}, z=${origin.z()}`);
}

Подшаги (substeps):

  • Второй параметр stepSimulation(deltaTime, maxSubSteps) задает максимальное число подшагов для более точной симуляции при изменении времени кадра.
  • Если кадр занимает слишком много времени, движок автоматически делит его на несколько подшагов для стабильности.

Применение сил и импульсов

Для управления объектами используются методы:

  • applyCentralForce(btVector3 force) — приложить силу к центру массы.
  • applyCentralImpulse(btVector3 impulse) — мгновенный импульс к центру массы.
  • applyTorque(btVector3 torque) — вращающий момент.
  • applyTorqueImpulse(btVector3 torqueImpulse) — мгновенный вращающий импульс.

Пример:

const force = new Ammo.btVector3(0, 50, 0);
body.applyCentralImpulse(force);

Ограничения и соединения

Ammo.js поддерживает различные типы соединений (constraints):

  • btPoint2PointConstraint — шарнирная связь.
  • btHingeConstraint — шарнир с ограничением вращения.
  • btSliderConstraint — движение вдоль одной оси.
  • btConeTwistConstraint — конусное вращение, ограниченное по углам.

Создание шарнирной связи между двумя телами:

const pivotInA = new Ammo.btVector3(0, 1, 0);
const pivotInB = new Ammo.btVector3(0, -1, 0);
const constraint = new Ammo.btPoint2PointConstraint(bodyA, bodyB, pivotInA, pivotInB);
dynamicsWorld.addConstraint(constraint, true);

Утилизация объектов

Ammo.js использует ручное управление памятью. Для предотвращения утечек необходимо удалять объекты после использования:

Ammo.destroy(body);
Ammo.destroy(boxShape);
Ammo.destroy(motionState);

Работа с нестандартными формами

btConvexHullShape позволяет создавать сложные формы:

const points = [ [1,1,1], [-1,1,1], [-1,-1,1], [1,-1,1] ];
const hullShape = new Ammo.btConvexHullShape();
points.forEach(p => hullShape.addPoint(new Ammo.btVector3(p[0], p[1], p[2])));

Использование выпуклых форм увеличивает реализм столкновений при сохранении стабильности симуляции.


Оптимизация производительности

  • Сокращение числа динамических тел — тяжелый фактор в производительности.
  • Использование простых форм вместо сложных мешей для коллизий.
  • Контроль подшагов — уменьшение maxSubSteps снижает точность, но ускоряет симуляцию.
  • Очистка памяти после удаления объектов.

Интеграция с рендерингом

Для визуализации физики часто используют Three.js:

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

function syncPhysics() {
    const transform = new Ammo.btTransform();
    body.getMotionState().getWorldTransform(transform);
    const origin = transform.getOrigin();
    mesh.position.set(origin.x(), origin.y(), origin.z());
}

Поддержание синхронизации между физическим телом и графическим объектом является критическим для корректного отображения сцены.


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