Для работы с физикой в Ammo.js необходимо создать
динамический мир, настроить параметры гравитации и интеграции. Основой
служит класс btDiscreteDynamicsWorld, который объединяет
механизмы столкновений и симуляцию динамики тел.
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.8, 0));
Ключевой момент: dispatcher отвечает за
обработку столкновений, broadphase ускоряет поиск
потенциальных столкновений, а solver решает систему
импульсов для динамических объектов.
Любое физическое тело в Ammo.js создается через сочетание
формы коллизии (btCollisionShape) и
тела (btRigidBody). Формы могут быть
простыми — коробка, сфера, цилиндр — или составными через
btCompoundShape.
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 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, boxShape, localInertia);
const body = new Ammo.btRigidBody(rbInfo);
dynamicsWorld.addRigidBody(body);
Особенность: расчёт localInertia
обязателен для динамических объектов, чтобы симулятор корректно учитывал
момент инерции при вращении.
ContactPairTest в Ammo.js используется для проверки наличия контакта между двумя конкретными телами. Этот механизм полезен при реализации игрового столкновения или детекции физических событий.
function checkContact(bodyA, bodyB) {
const dispatcher = dynamicsWorld.getDispatcher();
const numManifolds = dispatcher.getNumManifolds();
for (let i = 0; i < numManifolds; i++) {
const contactManifold = dispatcher.getManifoldByIndexInternal(i);
const obA = contactManifold.getBody0();
const obB = contactManifold.getBody1();
if ((obA === bodyA && obB === bodyB) || (obA === bodyB && obB === bodyA)) {
const numContacts = contactManifold.getNumContacts();
if (numContacts > 0) {
return true;
}
}
}
return false;
}
Разбор кода:
dispatcher.getNumManifolds() возвращает количество всех
текущих контактных манифолдов.contactManifold содержит пары тел и их точки
контакта.numContacts > 0 подтверждает наличие
физического столкновения.Такой подход позволяет детектировать коллизии между конкретными объектами без необходимости обработки всех столкновений мира.
Контактные точки имеют массу, коэффициент трения и реституции. Для
управления ими можно использовать методы setFriction и
setRestitution у btRigidBody:
body.setFriction(0.8);
body.setRestitution(0.5);
Эти значения играют важную роль при проверке контактов, особенно если требуется фильтровать касания только с определенной реакцией.
Симуляция в Ammo.js выполняется через вызов
stepSimulation. Для корректной работы ContactPairTest
обновление мира должно происходить до проверки контактов:
const deltaTime = 1 / 60; // 60 FPS
dynamicsWorld.stepSimulation(deltaTime, 10);
if (checkContact(bodyA, bodyB)) {
console.log('Объекты столкнулись');
}
Особенности:
maxSubSteps увеличивает точность
интеграции при высоких скоростях объектов.stepSimulation обновляет позиции, ориентации и
создаёт новые контактные манифолды.collisionGroup и collisionMask позволяет
проверять контакты только между выбранными классами объектов.btPersistentManifold для
анализа подробной информации о каждой точке контакта: позиция, нормаль,
сила импульса.const contactPoint = contactManifold.getContactPoint(j);
const pointPosition = contactPoint.getPositionWorldOnA();
const normal = contactPoint.getNormalWorldOnB();
Эти данные позволяют реализовать реалистичную реакцию на столкновение, включая эффекты отскока, трения и распределения сил.
Для интеграции с рендерингом (например, WebGL или Three.js) позиции
объектов берутся из motionState:
const transform = new Ammo.btTransform();
body.getMotionState().getWorldTransform(transform);
const origin = transform.getOrigin();
mesh.position.set(origin.x(), origin.y(), origin.z());
Такой подход синхронизирует физику и графику, сохраняя актуальность проверок ContactPairTest.
stepSimulation.Контроль точек контакта и проверка конкретных пар тел является фундаментальной частью взаимодействия динамических объектов в Ammo.js, позволяя реализовать сложные физические сценарии в браузерных приложениях.