ContactPairTest: проверка пары тел

Для работы с физикой в Ammo.js необходимо создать динамический мир, настроить параметры гравитации и интеграции. Основой служит класс btDiscreteDynamicsWorld, который объединяет механизмы столкновений и симуляцию динамики тел.

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.8, 0));

Ключевой момент: dispatcher отвечает за обработку столкновений, broadphase ускоряет поиск потенциальных столкновений, а solver решает систему импульсов для динамических объектов.


Создание тел и коллизий

Любое физическое тело в Ammo.js создается через сочетание формы коллизии (btCollisionShape) и тела (btRigidBody). Формы могут быть простыми — коробка, сфера, цилиндр — или составными через btCompoundShape.

const boxShape = new Ammo.btBoxShape(new Ammo.btVector3(1, 1, 1));
const mass = 1;
const localInertia = new Ammo.btVector3(0, 0, 0);
boxShape.calculateLocalInertia(mass, localInertia);

const transform = new Ammo.btTransform();
transform.setIdentity();
transform.setOrigin(new Ammo.btVector3(0, 10, 0));

const motionState = new Ammo.btDefaultMotionState(transform);
const rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, motionState, boxShape, localInertia);
const body = new Ammo.btRigidBody(rbInfo);

dynamicsWorld.addRigidBody(body);

Особенность: расчёт localInertia обязателен для динамических объектов, чтобы симулятор корректно учитывал момент инерции при вращении.


ContactPairTest: проверка пары тел

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

function checkContact(bodyA, bodyB) {
    const dispatcher = dynamicsWorld.getDispatcher();
    const numManifolds = dispatcher.getNumManifolds();

    for (let i = 0; i < numManifolds; i++) {
        const contactManifold = dispatcher.getManifoldByIndexInternal(i);
        const obA = contactManifold.getBody0();
        const obB = contactManifold.getBody1();

        if ((obA === bodyA && obB === bodyB) || (obA === bodyB && obB === bodyA)) {
            const numContacts = contactManifold.getNumContacts();
            if (numContacts > 0) {
                return true;
            }
        }
    }
    return false;
}

Разбор кода:

  • dispatcher.getNumManifolds() возвращает количество всех текущих контактных манифолдов.
  • Каждый contactManifold содержит пары тел и их точки контакта.
  • Проверка numContacts > 0 подтверждает наличие физического столкновения.

Такой подход позволяет детектировать коллизии между конкретными объектами без необходимости обработки всех столкновений мира.


Настройка параметров контакта

Контактные точки имеют массу, коэффициент трения и реституции. Для управления ими можно использовать методы setFriction и setRestitution у btRigidBody:

body.setFriction(0.8);
body.setRestitution(0.5);
  • Friction влияет на сопротивление скольжению при соприкосновении.
  • Restitution задает упругость столкновения.

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


Динамическое обновление мира

Симуляция в Ammo.js выполняется через вызов stepSimulation. Для корректной работы ContactPairTest обновление мира должно происходить до проверки контактов:

const deltaTime = 1 / 60; // 60 FPS
dynamicsWorld.stepSimulation(deltaTime, 10);
if (checkContact(bodyA, bodyB)) {
    console.log('Объекты столкнулись');
}

Особенности:

  • Второй параметр maxSubSteps увеличивает точность интеграции при высоких скоростях объектов.
  • Вызов stepSimulation обновляет позиции, ориентации и создаёт новые контактные манифолды.

Полезные оптимизации

  1. Использование фильтров коллизий через collisionGroup и collisionMask позволяет проверять контакты только между выбранными классами объектов.
  2. Сборка контактных точек в массив для последующей логики (например, нанесение урона при столкновении).
  3. Использование btPersistentManifold для анализа подробной информации о каждой точке контакта: позиция, нормаль, сила импульса.
const contactPoint = contactManifold.getContactPoint(j);
const pointPosition = contactPoint.getPositionWorldOnA();
const normal = contactPoint.getNormalWorldOnB();

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


Совместное использование с визуализацией

Для интеграции с рендерингом (например, WebGL или Three.js) позиции объектов берутся из motionState:

const transform = new Ammo.btTransform();
body.getMotionState().getWorldTransform(transform);
const origin = transform.getOrigin();
mesh.position.set(origin.x(), origin.y(), origin.z());

Такой подход синхронизирует физику и графику, сохраняя актуальность проверок ContactPairTest.


Итоговая схема работы

  1. Создание мира и тел.
  2. Настройка формы коллизий и физических свойств.
  3. Шаг симуляции через stepSimulation.
  4. Проверка контакта между конкретными объектами через перебор манифолдов.
  5. Использование контактной информации для логики приложения или визуальных эффектов.

Контроль точек контакта и проверка конкретных пар тел является фундаментальной частью взаимодействия динамических объектов в Ammo.js, позволяя реализовать сложные физические сценарии в браузерных приложениях.