Для использования Ammo.js сначала необходимо создать физический мир, определить его параметры и объекты. Основные элементы физического мира:
Пример создания динамического мира:
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));
Объекты в Ammo.js представляют собой rigid bodies. Основные шаги:
Пример для куба:
const size = 1;
const mass = 1;
const shape = new Ammo.btBoxShape(new Ammo.btVector3(size / 2, size / 2, size / 2));
const localInertia = new Ammo.btVector3(0, 0, 0);
shape.calculateLocalInertia(mass, localInertia);
const transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(0, 10, 0));
const motionState = new Ammo.btDefaultMotionState(transform);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, shape, localInertia);
const body = new Ammo.btRigidBody(rbInfo);
dynamicsWorld.addRigidBody(body);
requestAnimationFrameДля корректной работы физики и рендеринга применяется игровой
цикл, который использует requestAnimationFrame.
Ключевой момент — разделение:
stepSimulation)Основная структура цикла:
function animate() {
requestAnimationFrame(animate);
// Шаг симуляции: время в секундах, maxSubSteps, fixedTimeStep
const deltaTime = clock.getDelta(); // THREE.Clock, например
dynamicsWorld.stepSimulation(deltaTime, 10, 1/60);
// Обновление визуальных объектов
updateGraphics();
renderer.render(scene, camera);
}
animate();
Пояснения по параметрам
stepSimulation:
deltaTime — время между кадрами.maxSubSteps — максимальное количество шагов физики для
одного кадра, предотвращает пропуск столкновений при лаге.fixedTimeStep — фиксированный шаг симуляции (обычно
1/60 секунды).Такой паттерн гарантирует стабильную симуляцию, даже если частота кадров нестабильна.
Визуальные объекты, например в Three.js, необходимо обновлять на основе состояния rigid body:
function updateGraphics() {
const ms = body.getMotionState();
if (ms) {
const transform = new Ammo.btTransform();
ms.getWorldTransform(transform);
const p = transform.getOrigin();
const q = transform.getRotation();
mesh.position.set(p.x(), p.y(), p.z());
mesh.quaternion.set(q.x(), q.y(), q.z(), q.w());
}
}
Ключевой момент: визуальная синхронизация происходит после шага симуляции, чтобы движения объектов отображались корректно.
Ammo.js может быть ресурсоёмким при большом количестве объектов. Для оптимизации:
btDbvtBroadphase для динамических
объектов.maxSubSteps и
фиксированный fixedTimeStep.Ammo.js предоставляет доступ к контактным точкам через dispatcher:
const numManifolds = dispatcher.getNumManifolds();
for (let i = 0; i < numManifolds; i++) {
const contactManifold = dispatcher.getManifoldByIndexInternal(i);
const numContacts = contactManifold.getNumContacts();
if (numContacts > 0) {
const body0 = contactManifold.getBody0();
const body1 = contactManifold.getBody1();
// Логика реакции на столкновение
}
}
Это позволяет реализовывать эффекты удара, триггеры и другие игровые механики.
В связке с Three.js или Babylon.js физические объекты управляют
только положением и вращением. Рендеринг остаётся отдельным слоем,
обновляющимся после stepSimulation. Такой подход:
Для стабильности используют фиксированный шаг
симуляции, обычно 1/60 секунды. При высоком FPS шаг может
повторяться несколько раз (maxSubSteps > 1). При низком
FPS физика не теряет точность благодаря нескольким подшагам. Такой
подход предотвращает «проваливание» объектов сквозь коллайдеры.
Статья охватывает ключевые аспекты использования Ammo.js с паттерном
requestAnimationFrame + stepSimulation: создание мира,
физических тел, игровой цикл, синхронизацию графики и оптимизацию
производительности. Такой подход формирует стабильную и предсказуемую
симуляцию физики в браузере.