Ammo.js является JavaScript-портом физического движка Bullet Physics, и его ключевая особенность — возможность взаимодействия объектов через физические события. Механизм коллбэков в Ammo.js позволяет реагировать на столкновения, изменения состояния тел и другие физические события в симуляции.
Коллбэк — это функция, которая вызывается движком автоматически при наступлении определённого события. В Ammo.js такие события чаще всего связаны с столкновениями тел, событиями триггеров или изменениями состояния динамических объектов.
В Ammo.js обработка столкновений происходит на уровне
контактов тел. Основной объект для отслеживания
контактов — btCollisionWorld. Через него можно получить
информацию о каждом контакте между телами.
Классический подход:
let collisionConfiguration = new Ammo.btDefaultCollisionConfiguration();
let dispatcher = new Ammo.btCollisionDispatcher(collisionConfiguration);
let overlappingPairCache = new Ammo.btDbvtBroadphase();
let solver = new Ammo.btSequentialImpulseConstraintSolver();
let dynamicsWorld = new Ammo.btDiscreteDynamicsWorld(
dispatcher,
overlappingPairCache,
solver,
collisionConfiguration
);
dynamicsWorld.addRigidBody(rigidBody1);
dynamicsWorld.addRigidBody(rigidBody2);
let numManifolds = dispatcher.getNumManifolds();
for (let i = 0; i < numManifolds; i++) {
let contactManifold = dispatcher.getManifoldByIndexInternal(i);
let body0 = contactManifold.getBody0();
let body1 = contactManifold.getBody1();
let numContacts = contactManifold.getNumContacts();
for (let j = 0; j < numContacts; j++) {
let pt = contactManifold.getContactPoint(j);
if (pt.getDistance() < 0) {
let position = pt.getPositionWorldOnA();
let normal = pt.getNormalWorldOnB();
// Здесь можно обрабатывать столкновение
}
}
}
Ключевой момент: Ammo.js не имеет встроенного
глобального события onCollision. Все коллбэки реализуются
через перебор контактных манифолдов в цикле симуляции.
btCollisionWorld::ContactResultCallbackДля более удобной работы можно использовать класс
Ammo.ConcreteContactResultCallback, который позволяет
отслеживать столкновения конкретного тела с любыми другими
объектами:
let callback = new Ammo.ConcreteContactResultCallback();
callback.addSingleResult = function(cp, colObj0Wrap, partId0, index0, colObj1Wrap, partId1, index1) {
let collisionPoint = cp.getPositionWorldOnA();
let collisionNormal = cp.getNormalWorldOnB();
console.log("Столкновение в точке:", collisionPoint.x(), collisionPoint.y(), collisionPoint.z());
};
dynamicsWorld.contactTest(rigidBody, callback);
Особенности:
contactTest проверяет столкновения конкретного
тела.addSingleResult вызывается для каждого
контакта.Ammo.ConcreteContactResultCallback.Ammo.js позволяет использовать триггерные объекты — тела, которые не реагируют физически на столкновения, но отслеживают проникновение других тел.
Для реализации триггеров:
CF_NO_CONTACT_RESPONSE:let shape = new Ammo.btBoxShape(new Ammo.btVector3(1,1,1));
let motionState = new Ammo.btDefaultMotionState(transform);
let rbInfo = new Ammo.btRigidBodyConstructionInfo(0, motionState, shape, new Ammo.btVector3(0,0,0));
let triggerBody = new Ammo.btRigidBody(rbInfo);
triggerBody.setCollisionFlags(triggerBody.getCollisionFlags() | Ammo.cf_noContactResponse);
dynamicsWorld.addRigidBody(triggerBody);
let triggerCallback = new Ammo.ConcreteContactResultCallback();
triggerCallback.addSingleResult = function(cp, colObj0Wrap, partId0, index0, colObj1Wrap, partId1, index1) {
console.log("Объект вошёл в триггер");
};
dynamicsWorld.contactTest(triggerBody, triggerCallback);
Ключевой момент: даже без физического отклика объекты можно отслеживать через коллбэки.
getUserIndex() или хранить ссылку на объект в
userPointer.let userPointer = {name: "player"};
rigidBody.setUserPointer(userPointer);
В коллбэке:
let obj = Ammo.castObject(colObj1Wrap.getCollisionObject().getUserPointer(), Ammo.btVector3);
if(obj.name === "player") { /* обработка */ }
Сокращение количества проверок Не проверять все
тела мира каждый кадр. Использовать contactTest или
contactPairTest для конкретных объектов.
Минимизация аллокаций Ammo.js работает через
WebAssembly. Частое создание btVector3 или
btTransform может замедлять симуляцию. Рекомендуется
использовать заранее созданные объекты и переиспользовать их.
Некоторые коллбэки могут реагировать не только на столкновения, но и на изменения состояния тел:
isActive()).Пример отслеживания падения объекта:
if(rigidBody.getLinearVelocity().length() > threshold) {
console.log("Объект движется слишком быстро");
}
Эти проверки обычно реализуются в основном игровом цикле, но по сути выполняют роль пользовательских коллбэков.
| Тип коллбэка | Использование | Методы |
|---|---|---|
| Столкновения | Реакция на контакт между телами | dispatcher.getManifoldByIndexInternal,
contactTest |
| Триггеры | Отслеживание пересечений без физической реакции | CF_NO_CONTACT_RESPONSE, contactTest |
| Динамика | Проверка состояния тел | getLinearVelocity, getWorldTransform,
isActive |
| Пользовательские | Полная кастомизация реакции | Наследование ConcreteContactResultCallback,
переопределение addSingleResult |
Механизм коллбэков в Ammo.js гибкий, но требует понимания работы диспетчера столкновений и структуры контактных манифолдов. Оптимальная организация коллбэков позволяет создавать сложные интерактивные физические сцены с минимальной нагрузкой на симуляцию.