ContactTest: проверка конкретного тела на столкновения

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


Основные понятия

  • RigidBody — тело, для которого проводится проверка. Оно может иметь динамический, статический или кинематический тип.
  • Manifold — структура, содержащая информацию о точках контакта между объектами, нормали столкновения и величину силы.
  • Callback — функция обратного вызова, которая вызывается для каждого столкновения тела с другими объектами. В Ammo.js она реализуется через наследование от встроенного класса Ammo.ConcreteContactResultCallback.

Создание и использование ContactTest

  1. Создание коллбэка

Для проверки контактов необходимо создать объект обратного вызова, наследующийся от Ammo.ConcreteContactResultCallback. В нём реализуется метод addSingleResult, который вызывается для каждой точки контакта:

class MyContactCallback extends Ammo.ConcreteContactResultCallback {
    constructor() {
        super();
        this.contacts = [];
    }

    addSingleResult(cp, colObj0Wrap, partId0, index0, colObj1Wrap, partId1, index1) {
        // cp — информация о точке контакта
        // colObj1Wrap — объект, с которым произошло столкновение
        this.contacts.push({
            contactPoint: cp.get_m_positionWorldOnB(),
            otherBody: Ammo.castObject(colObj1Wrap.getCollisionObject(), Ammo.btRigidBody)
        });
        return 0; // возвращаемое значение обычно игнорируется
    }
}
  1. Вызов метода contactTest

После создания callback выполняется проверка столкновений:

const callback = new MyContactCallback();
physicsWorld.contactTest(rigidBody, callback);

if (callback.contacts.length > 0) {
    console.log('Тело столкнулось с другими объектами', callback.contacts);
}

Метод contactTest принимает два аргумента:

  • rigidBody — тело, для которого проверяется контакт.
  • callback — объект с методом addSingleResult.

Особенности работы ContactTest

  • Одно тело против всех: ContactTest проверяет столкновения конкретного объекта со всеми телами в мире.
  • Без физического ответа: метод не изменяет позиции и скорости тел, он лишь собирает информацию о контактах.
  • Подходит для фильтрации объектов: можно определить столкновение с конкретным типом тел или тегом.

Пример фильтрации по маске:

class FilteredContactCallback extends Ammo.ConcreteContactResultCallback {
    constructor(filterFunc) {
        super();
        this.contacts = [];
        this.filterFunc = filterFunc;
    }

    addSingleResult(cp, colObj0Wrap, partId0, index0, colObj1Wrap, partId1, index1) {
        const otherBody = Ammo.castObject(colObj1Wrap.getCollisionObject(), Ammo.btRigidBody);
        if (this.filterFunc(otherBody)) {
            this.contacts.push(otherBody);
        }
        return 0;
    }
}

// Проверяем столкновение только с динамическими телами
const callback = new FilteredContactCallback(body => otherBody.getCollisionFlags() & Ammo.cf_dynamicObject);
physicsWorld.contactTest(rigidBody, callback);

Контроль производительности

Метод ContactTest более эффективен, чем постоянный обход всех коллизий через симуляцию шагов, поскольку:

  • Не создаёт новые столкновения или контактные манифольды в мире.
  • Вызывает callback только для текущего состояния тела.
  • Позволяет использовать фильтры, чтобы обрабатывать лишь нужные объекты.

Для больших сцен с множеством тел рекомендуется использовать маски коллизий и grps/filters, чтобы сократить количество проверяемых контактов.


Взаимодействие с другими методами

  • contactPairTest — проверяет столкновения между двумя конкретными телами, в отличие от contactTest, который проверяет объект против всех.
  • rayTest / convexSweepTest — аналогичные методы, но используют луч или объем вместо конкретного тела для проверки пересечений.

Использование ContactTest совместно с contactPairTest позволяет организовать гибкую систему обнаружения столкновений, где можно одновременно проверять и конкретные пары тел, и все столкновения одного объекта с миром.


Практические сценарии применения

  1. Игровая логика: проверка, наступил ли персонаж на платформу или триггер.
  2. Системы повреждений: определение, какое тело получило удар без полного физического взаимодействия.
  3. AI и навигация: проверка наличия препятствий перед движением объекта.
  4. Физические сенсоры: симуляция датчиков столкновений, не влияя на динамику объектов.

Особенности реализации в Ammo.js

  • addSingleResult получает объект btManifoldPoint, из которого можно извлечь:

    • Позицию контакта (get_m_positionWorldOnB() / get_m_positionWorldOnA())
    • Нормаль столкновения (get_m_normalWorldOnB())
    • Импульс контакта (get_m_appliedImpulse())
  • Необходимо кастить обёртки объектов через Ammo.castObject, чтобы работать с btRigidBody.

  • Контактный callback создаётся через new Ammo.ConcreteContactResultCallback() или через наследование, так как прямое использование абстрактного класса невозможно.


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