Реализация onContactProcessed

В Ammo.js обработка контактов между телами в физическом мире происходит через коллбэки, один из которых — onContactProcessed. Этот метод используется для получения информации о столкновениях и, при необходимости, вмешательства в физические расчёты на уровне контакта. Основное назначение — кастомизация реакции объектов на столкновение без модификации всей симуляции.

Принцип работы

onContactProcessed вызывается для каждой пары контактирующих объектов после того, как Bullet вычислил контактные точки, нормали и импульсы, но до применения их к объектам. Функция имеет сигнатуру:

function onContactProcessed(cp, colObj0Wrap, colObj1Wrap) { ... }
  • cp (ContactPoint) — объект, содержащий данные о точке контакта: позицию, нормаль, импульс, глубину проникновения.
  • colObj0Wrap / colObj1Wrap (CollisionObjectWrapper) — обёртки для тел, участвующих в контакте. Через них можно получить физические объекты btRigidBody и их свойства.

Получение и модификация данных о контакте

Контактная точка предоставляет доступ к ключевым параметрам:

let position = cp.get_m_positionWorldOnA();
let normal = cp.get_m_normalWorldOnB();
let distance = cp.get_m_distance1();
  • get_m_positionWorldOnA() и get_m_positionWorldOnB() возвращают вектор позиции контакта в мировых координатах для объектов A и B.
  • get_m_normalWorldOnB() — нормаль контакта, направленная от объекта B к объекту A.
  • get_m_distance1() — глубина проникновения (отрицательная при пересечении).

Изменять напрямую нормаль и позицию нежелательно, но можно влиять на силы, которые применяются к телам:

let impulse = cp.getAppliedImpulse();
cp.setAppliedImpulse(impulse * 0.5);

Это позволяет реализовать эффекты «мягких столкновений» или кастомной амортизации.

Привязка коллбэка к объекту

Чтобы коллбэк работал, его необходимо зарегистрировать для btCollisionObject или btRigidBody. Пример для btRigidBody:

let body = new Ammo.btRigidBody(rbInfo);

body.setContactProcessingThreshold(0.1); // минимальное расстояние для обработки контакта
body.onContactProces sed = function(cp, colObj0Wrap, colObj1Wrap) {
    let normal = cp.get_m_normalWorldOnB();
    let impulse = cp.getAppliedImpulse();
    if (impulse > 10) {
        console.log('Сильный удар', impulse, normal.x(), normal.y(), normal.z());
    }
    return true; // true — разрешить физическую обработку
};
  • setContactProcessingThreshold — определяет минимальное расстояние до столкновения, при котором будет вызван коллбэк.
  • Возвращаемое значение true/false определяет, будет ли Bullet применять стандартные физические силы после вызова коллбэка. Возврат false позволяет полностью перехватить обработку контакта.

Примеры применения

  1. Ограничение силы контакта Для создания мягких столкновений или защиты объектов от разрушения можно уменьшать импульс:
cp.setAppliedImpulse(Math.min(cp.getAppliedImpulse(), 5));
  1. Фильтрация столкновений Можно игнорировать контакты между определёнными типами объектов:
if (colObj0Wrap.getCollisionObject().getUserIndex() === 1 &&
    colObj1Wrap.getCollisionObject().getUserIndex() === 2) {
    return false; // контакт не обрабатывается физикой
}
  1. Реализация кастомного звукового эффекта или визуализации По силе и нормали контакта можно воспроизводить звук или создавать частицы:
let force = cp.getAppliedImpulse();
if (force > 15) {
    spawnParticles(cp.get_m_positionWorldOnA(), force);
}

Особенности и ограничения

  • onContactProcessed вызывается каждую симуляционную итерацию для всех активных контактов, поэтому тяжёлая логика в коллбэке может серьёзно замедлить симуляцию.
  • Изменение физических параметров через коллбэк должно быть осторожным, иначе возникают нестабильности и «взлетающие» объекты.
  • Для корректной работы необходимо устанавливать контактный порог через setContactProcessingThreshold, иначе некоторые контакты не будут передаваться коллбэку.

Интеграция с миром

Коллбэк работает в контексте btDiscreteDynamicsWorld и реагирует на все объекты, зарегистрированные в этом мире. Пример установки нескольких объектов с разными обработчиками:

let world = new Ammo.btDiscreteDynamicsWorld(...);

let boxBody = createRigidBody(boxShape, mass, position);
let sphereBody = createRigidBody(sphereShape, mass, position);

boxBody.onContactProces sed = handleBoxContacts;
sphereBody.onContactProces sed = handleSphereContacts;

world.addRigidBody(boxBody);
world.addRigidBody(sphereBody);

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


onContactProcessed предоставляет мощный механизм вмешательства в физическую симуляцию на уровне отдельных точек контакта. Управление импульсами, фильтрация взаимодействий и генерация событий — всё это возможно без изменения ядра симулятора. Правильное использование этого метода позволяет создавать реалистичную динамику и контролируемые реакции объектов в Ammo.js.