Интеграция с PlayCanvas

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

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):

  • Hinge (btHingeConstraint) – поворот вокруг одной оси.
  • Point to Point (btPoint2PointConstraint) – фиксированная точка между двумя телами.
  • Slider (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 API

Для более удобного управления можно создать компонент PlayCanvas AmmoRigidBody, который:

  • Хранит ссылку на Ammo.js rigid body.
  • Автоматически синхронизирует позицию и вращение.
  • Обрабатывает добавление и удаление тела из физического мира.
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.
  • Использование ограничений и связей для сложных объектов.
  • Оптимизация через статические тела и широкую фазу коллизий.

Эта структура позволяет создавать реалистичные физические симуляции в браузере с высокой производительностью и гибкостью.