В библиотеке Ammo.js, являющейся JavaScript-портом
популярного физического движка Bullet Physics, объекты физических тел
(btRigidBody), коллайдеров (btCollisionObject)
и других элементов сцены могут хранить пользовательские
данные, привязанные напрямую к объекту. Механизм передачи таких
данных реализуется через методы setUserPointer и
getUserPointer.
setUserPointer и getUserPointerКаждый объект, унаследованный от btCollisionObject,
обладает внутренним полем user pointer, которое по
умолчанию пустое. Оно используется для хранения ссылки на произвольный
JavaScript-объект, связанный с конкретным физическим телом:
const body = new Ammo.btRigidBody(rbInfo);
body.setUserPointer({ name: "Player", health: 100 });
setUserPointer(pointer) — сохраняет
произвольный объект или ссылку на объект внутри физического тела.getUserPointer() — возвращает ранее
сохранённый объект, либо null, если указатель не
установлен.Преимущество использования этого механизма заключается в том, что данные живут вместе с объектом в физическом мире, упрощая связь между визуальной частью сцены и физическим движком.
Чаще всего setUserPointer используется для идентификации
объектов при обработке столкновений. События коллизий в Ammo.js
возвращают объекты типа btCollisionObject, и с помощью
пользовательского указателя можно определить, какой визуальный объект
или логическая сущность находится в столкновении.
Пример обработки столкновений:
const dispatcher = dynamicsWorld.getDispatcher();
const numManifolds = dispatcher.getNumManifolds();
for (let i = 0; i < numManifolds; i++) {
const contactManifold = dispatcher.getManifoldByIndexInternal(i);
const bodyA = contactManifold.getBody0();
const bodyB = contactManifold.getBody1();
const userA = Ammo.castObject(bodyA.getUserPointer(), Ammo.wrapPointer) || null;
const userB = Ammo.castObject(bodyB.getUserPointer(), Ammo.wrapPointer) || null;
if (userA && userB) {
console.log("Столкновение между:", userA.name, "и", userB.name);
}
}
Здесь каждый объект получает свою идентификацию, что позволяет связать физическое столкновение с логикой игры или симуляции.
Ammo.js использует внутренний C++ движок Bullet, поэтому при передаче
JS-объекта через setUserPointer фактически создается
обертка на указатель. Это важно учитывать:
const metadata = { id: 42, type: "Enemy" };
body.setUserPointer(metadata);
Ammo.wrapPointer, чтобы корректно обработать указатели из
C++.Механизм user pointer работает не только с
btRigidBody, но и с:
btCollisionObjectbtGhostObjectbtSoftBodyПример использования с btGhostObject для определения,
какие объекты находятся внутри зоны триггера:
const ghost = new Ammo.btGhostObject();
ghost.setUserPointer({ zoneName: "SpawnArea" });
dynamicsWorld.addCollisionObject(
ghost,
Ammo.CollisionFilterGroups.Trigger,
Ammo.CollisionFilterGroups.All
);
В этом случае при проверке столкновений можно узнать, какой игрок или объект вошёл в область триггера.
Ammo.destroy(body) рекомендуется обнулить user
pointer:body.setUserPointer(null);
Ammo.destroy(body);
getUserPointer.const mesh = { meshId: "box1" };
const body = new Ammo.btRigidBody(rbInfo);
body.setUserPointer(mesh);
const data = { id: 7, faction: "Allies" };
body.setUserPointer(data);
// В обработчике столкновений:
const user = body.getUserPointer();
console.log(user.id, user.faction);
triggerZone.setUserPointer({ event: "HealZone", value: 50 });
if (playerBody.getUserPointer() === triggerZone.getUserPointer()) {
playerHealth += triggerZone.getUserPointer().value;
}
Механизм setUserPointer и getUserPointer
обеспечивает прямую связь между физическими объектами Ammo.js и любыми
данными JavaScript, позволяя реализовывать эффективную обработку
коллизий, триггеров и игровых логик, сохраняя при этом
структуру и ясность кода.