Ammo.js представляет собой JavaScript-порт известного физического движка Bullet Physics. Он обеспечивает реалистичную динамику твердых тел, столкновения и ограничительные связи. В PlayCanvas интеграция Ammo.js позволяет добавлять физику на уровне объектов сцены, взаимодействовать с ними через скрипты и создавать сложные симуляции.
Физический мир в Ammo.js создается с помощью объекта
btDiscreteDynamicsWorld. Основные компоненты:
Конфигурация столкновений (Collision
Configuration) Используется объект
Ammo.btDefaultCollisionConfiguration, который управляет
алгоритмами обнаружения коллизий.
Диспетчер столкновений (Collision Dispatcher)
Ammo.btCollisionDispatcher получает конфигурацию и
обрабатывает события столкновений между объектами.
Система поиска широких фаз (Broadphase
Interface) Например, Ammo.btDbvtBroadphase
оптимизирует поиск потенциальных столкновений, повышая
производительность.
Солвер (Constraint Solver)
Ammo.btSequentialImpulseConstraintSolver обеспечивает
расчет сил, импульсов и ограничений.
Пример инициализации мира:
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 body).
Формы бывают разнообразные: btBoxShape,
btSphereShape, btCylinderShape и т.д.
Пример создания куба:
const boxShape = new Ammo.btBoxShape(new Ammo.btVector3(1, 1, 1));
const mass = 1;
const transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(0, 10, 0));
const motionState = new Ammo.btDefaultMotionState(transform);
const localInertia = new Ammo.btVector3(0, 0, 0);
boxShape.calculateLocalInertia(mass, localInertia);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, boxShape, localInertia);
const rigidBody = new Ammo.btRigidBody(rbInfo);
dynamicsWorld.addRigidBody(rigidBody);
Ключевые моменты:
calculateLocalInertia вычисляет момент инерции для тел
с массой. Для статических объектов масса равна 0.btDefaultMotionState синхронизирует положение и
вращение тела.PlayCanvas использует систему компонентов (pc.Entity)
для объектов сцены. Для синхронизации с Ammo.js требуется обновлять
позицию и ориентацию:
function updatePhysics(deltaTime) {
dynamicsWorld.stepSimulation(deltaTime, 10);
rigidBodies.forEach(rb => {
const motionState = rb.getMotionState();
if (motionState) {
const transform = new Ammo.btTransform();
motionState.getWorldTransform(transform);
const origin = transform.getOrigin();
const rotation = transform.getRotation();
const entity = rb.entity; // Связь с PlayCanvas entity
entity.setPosition(origin.x(), origin.y(), origin.z());
entity.setRotation(rotation.x(), rotation.y(), rotation.z(), rotation.w());
}
});
}
Ammo.js поддерживает разнообразные связи
(constraints):
btHingeConstraint) – поворот
вокруг одной оси.btPoint2PointConstraint) – фиксированная точка между двумя
телами.btSliderConstraint) –
скольжение вдоль одной оси с ограничением вращения.Пример шарнирного соединения:
const hinge = new Ammo.btHingeConstraint(bodyA, bodyB, pivotA, pivotB, axisA, axisB, true);
dynamicsWorld.addConstraint(hinge, true);
Параметр true в addConstraint включает
коллизии между связанными телами.
btDbvtBroadphase) для больших сцен.stepSimulation, используя
фиксированный timestep.Ammo.destroy.Для более удобного управления можно создать компонент PlayCanvas
AmmoRigidBody, который:
pc.script.create('ammoRigidBody', function (app) {
return {
initialize: function() {
this.entity.rigidBody = createRigidBody(this.entity);
},
update: function(dt) {
syncEntityWithRigidBody(this.entity.rigidBody, this.entity);
}
};
});
Такой подход упрощает масштабирование сцены и интеграцию с другими системами PlayCanvas, такими как анимация и столкновения пользовательских скриптов.
Ammo.js поддерживает raycasting и pick &
drag через btRigidBody. Для PlayCanvas это
позволяет реализовать:
Пример raycast:
const rayFrom = new Ammo.btVector3(camPos.x, camPos.y, camPos.z);
const rayTo = new Ammo.btVector3(targetPos.x, targetPos.y, targetPos.z);
const rayCallback = new Ammo.ClosestRayResultCallback(rayFrom, rayTo);
dynamicsWorld.rayTest(rayFrom, rayTo, rayCallback);
if (rayCallback.hasHit()) {
const hitBody = rayCallback.get_m_collisionObject();
// взаимодействие с телом
}
Использование этих возможностей позволяет создавать интерактивные и динамические сцены, полностью управляемые физикой.
Интеграция Ammo.js с PlayCanvas строится на следующих принципах:
motionState или компоненты
PlayCanvas.Эта структура позволяет создавать реалистичные физические симуляции в браузере с высокой производительностью и гибкостью.