Ammo.js — это JavaScript-порт физического движка Bullet, ориентированный на работу с трёхмерной физикой в браузере. Одним из ключевых аспектов физического симулятора является отслеживание столкновений объектов. Логирование контактов и коллизий позволяет анализировать взаимодействия объектов, отладить физическое поведение сцены и реализовать игровую механику на основе столкновений.
В Ammo.js все физические объекты создаются как экземпляры
btRigidBody, прикреплённые к
btDiscreteDynamicsWorld. Каждое столкновение фиксируется на
уровне контактного диспетчера
(btDispatcher). Основной подход к логированию контактов
состоит в обходе всех манифолд-контактов (contact manifolds) после шага
симуляции:
const dispatcher = physicsWorld.getDispatcher();
const numManifolds = dispatcher.getNumManifolds();
for (let i = 0; i < numManifolds; i++) {
const contactManifold = dispatcher.getManifoldByIndexInternal(i);
const body0 = contactManifold.getBody0();
const body1 = contactManifold.getBody1();
const numContacts = contactManifold.getNumContacts();
for (let j = 0; j < numContacts; j++) {
const contactPoint = contactManifold.getContactPoint(j);
if (contactPoint.getDistance() < 0) {
const pointA = contactPoint.getPositionWorldOnA();
const pointB = contactPoint.getPositionWorldOnB();
const normal = contactPoint.getNormalWorldOnB();
console.log(`Collision between body0 and body1`);
console.log(`Point A: (${pointA.x()}, ${pointA.y()}, ${pointA.z()})`);
console.log(`Point B: (${pointB.x()}, ${pointB.y()}, ${pointB.z()})`);
console.log(`Normal: (${normal.x()}, ${normal.y()}, ${normal.z()})`);
}
}
}
Ключевые моменты кода:
getNumManifolds() возвращает количество контактных
манифолд объектов, участвующих в столкновениях.getBody0() и getBody1() — ссылки на
объекты, участвующие в конкретном столкновении.getNumContacts() и getContactPoint(j)
позволяют пройтись по всем точкам контакта между телами.getDistance() определяет, соприкасаются ли объекты
(< 0 означает пересечение).getPositionWorldOnA() и
getPositionWorldOnB() дают координаты точек контакта в
мировых координатах.getNormalWorldOnB() возвращает вектор нормали
столкновения относительно второго тела.Для оптимизации логирования и предотвращения лишних вычислений можно
использовать маски столкновений. Каждое тело получает
collisionFilterGroup и collisionFilterMask при
создании btRigidBody:
const group = 1; // Например, группа динамических объектов
const mask = 2; // Будет сталкиваться только с объектами маски 2
physicsWorld.addRigidBody(body, group, mask);
Использование масок позволяет фильтровать объекты, чтобы логировать только интересующие взаимодействия, например, игрока с препятствиями или снаряды с окружением.
Ammo.js предоставляет методы для получения информации о силе взаимодействия. Для каждой точки контакта можно получить силу реакции и импульс:
const appliedImpulse = contactPoint.getAppliedImpulse();
console.log(`Impulse: ${appliedImpulse}`);
getAppliedImpulse() возвращает величину импульса,
действующего в точке контакта.Ammo.js поддерживает создание собственных обработчиков столкновений
через наследование btCollisionWorld::ContactResultCallback.
Пример пользовательского коллбека:
class MyContactCallback extends Ammo.ConcreteContactResultCallback {
constructor() { super(); }
addSingleResult(cp, colObj0Wrap, partId0, index0, colObj1Wrap, partId1, index1) {
const body0 = Ammo.castObject(colObj0Wrap.getCollisionObject(), Ammo.btRigidBody);
const body1 = Ammo.castObject(colObj1Wrap.getCollisionObject(), Ammo.btRigidBody);
console.log(`Custom callback: collision detected between body0 and body1`);
return 0; // Возвращаем 0 для продолжения обработки
}
}
const callback = new MyContactCallback();
physicsWorld.contactTest(body, callback);
Особенности:
contactTest() проверяет столкновения конкретного тела с
остальными объектами в мире.addSingleResult() вызывается для каждой точки контакта,
давая возможность выполнять любые действия при столкновении, включая
игровую логику или спецэффекты.contactPairTest()
между двумя телами или группами тел.contactTest() для
оптимизации.three.js для наглядного
дебага.Логирование контактов тесно связано с реализацией динамических эффектов:
Ammo.js предоставляет мощные инструменты для детального анализа коллизий, позволяя управлять физикой сцены на уровне точек контакта, импульсов и нормалей. Это фундаментальная основа для разработки реалистичных и управляемых физических взаимодействий в браузерных 3D-приложениях.