Ghost-объекты: btGhostObject и btPairCachingGhostObject

В физическом движке, основанном на библиотеке Bullet Physics, существует класс объектов, которые участвуют в широкофазной и узкофазной проверке пересечений, но не вступают в физическое взаимодействие с другими телами. В JavaScript-порте Ammo.js эти объекты представлены классами:

  • btGhostObject
  • btPairCachingGhostObject

Ghost-объекты предназначены для:

  • триггерных зон;
  • систем обнаружения пересечений;
  • сенсоров персонажей;
  • проверки доступности пространства;
  • реализации кастомной логики столкновений без физического отклика.

Главная особенность — объект регистрируется в физическом мире, но не генерирует сил реакции.


Архитектура ghost-объектов

Ghost-объекты являются наследниками btCollisionObject. Это означает:

  • наличие формы столкновения (btCollisionShape);
  • наличие мирового преобразования (btTransform);
  • участие в broadphase;
  • участие в narrowphase.

Однако они:

  • не имеют массы;
  • не интегрируются физическим солвером;
  • не создают контактных импульсов.

Класс btGhostObject

Основное назначение

btGhostObject — базовый класс триггерного объекта. Он отслеживает пересечения с другими телами и хранит список overlapping-объектов.

Создание

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

const shape = new Ammo.btBoxShape(new Ammo.btVector3(1, 1, 1));

const ghostObject = new Ammo.btGhostObject();
ghostObject.setCollisionShape(shape);
ghostObject.setWorldTransform(transform);

Регистрация в мире

Ghost-объект необходимо добавить в физический мир через:

physicsWorld.addCollisionObject(
    ghostObject,
    collisionGroup,
    collisionMask
);

Важно корректно настроить:

  • collisionGroup
  • collisionMask

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


Получение пересечений

После шага симуляции:

physicsWorld.stepSimulation(deltaTime, 10);

Можно получить список пересечений:

const numOverlapping = ghostObject.getNumOverlappingObjects();

for (let i = 0; i < numOverlapping; i++) {
    const overlappingObject = ghostObject.getOverlappingObject(i);
}

Метод getOverlappingObject(i) возвращает btCollisionObject.


Ограничения btGhostObject

btGhostObject не кэширует контактные пары. Это означает:

  • при большом количестве объектов возможна дополнительная нагрузка;
  • отсутствие доступа к детальной информации о контактах;
  • подходит для простых триггерных проверок.

Для более продвинутых сценариев используется btPairCachingGhostObject.


btPairCachingGhostObject

Расширенные возможности

btPairCachingGhostObject расширяет функциональность базового ghost-объекта:

  • хранит кэш контактных пар;
  • обеспечивает более стабильное отслеживание пересечений;
  • используется в системах персонажей (btKinematicCharacterController).

Внутренне использует pair cache, интегрированный в broadphase.

Создание

const ghostObject = new Ammo.btPairCachingGhostObject();
ghostObject.setCollisionShape(shape);
ghostObject.setWorldTransform(transform);

Добавление в мир выполняется аналогично btGhostObject.


Pair Cache и механизм работы

В broadphase каждый объект регистрируется через btBroadphaseProxy. btPairCachingGhostObject хранит btHashedOverlappingPairCache, позволяющий:

  • эффективно обновлять список пересечений;
  • избегать повторных проверок;
  • ускорять обработку больших сцен.

Это особенно важно в:

  • FPS-играх;
  • сценах с большим количеством триггерных зон;
  • системах навигации.

Флаги столкновений

Ghost-объекты требуют установки специального флага:

ghostObject.setCollisionFlags(
    ghostObject.getCollisionFlags() |
    Ammo.btCollisionObject.CF_NO_CONTACT_RESPONSE
);

Флаг CF_NO_CONTACT_RESPONSE гарантирует отсутствие физического отклика.

Дополнительно может применяться:

  • CF_CHARACTER_OBJECT — для объектов персонажей.

Типичный сценарий: триггерная зона

Шаг 1. Создание формы

const triggerShape = new Ammo.btBoxShape(
    new Ammo.btVector3(5, 2, 5)
);

Шаг 2. Настройка ghost-объекта

const trigger = new Ammo.btGhostObject();
trigger.setCollisionShape(triggerShape);
trigger.setWorldTransform(transform);

trigger.setCollisionFlags(
    trigger.getCollisionFlags() |
    Ammo.btCollisionObject.CF_NO_CONTACT_RESPONSE
);

Шаг 3. Добавление в мир

physicsWorld.addCollisionObject(
    trigger,
    1,
    -1
);

Шаг 4. Проверка пересечений

const count = trigger.getNumOverlappingObjects();

if (count > 0) {
    // объект внутри зоны
}

Ghost-объект и персонаж

В связке с btKinematicCharacterController применяется btPairCachingGhostObject. Причины:

  • постоянное отслеживание контактов с поверхностью;
  • проверка наличия пола;
  • корректная обработка ступенек и наклонных поверхностей.

Типичная архитектура:

btPairCachingGhostObject
        ↓
btConvexShape
        ↓
btKinematicCharacterController

Ghost-объект представляет физическое тело персонажа, а контроллер управляет его перемещением.


Отличия от rigid body

Характеристика btRigidBody btGhostObject
Масса Есть Нет
Участвует в солвере Да Нет
Генерирует импульсы Да Нет
Отслеживает пересечения Да Да
Используется как триггер Неудобно Да

Rigid body всегда создаёт физическую реакцию. Ghost-объект — только событие пересечения.


Работа с группами столкновений

Для корректной фильтрации используется битовая маска:

const GROUP_TRIGGER = 1 << 4;
const GROUP_DYNAMIC = 1 << 1;

physicsWorld.addCollisionObject(
    ghostObject,
    GROUP_TRIGGER,
    GROUP_DYNAMIC
);

Это позволяет:

  • исключать триггеры друг от друга;
  • реагировать только на динамические тела;
  • оптимизировать broadphase.

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

Когда использовать btGhostObject

  • небольшое количество триггеров;
  • простые проверки входа/выхода;
  • отсутствие необходимости в детальной информации.

Когда использовать btPairCachingGhostObject

  • персонажи;
  • сложная логика столкновений;
  • большое количество объектов;
  • высокая частота проверок.

Pair caching снижает нагрузку при сложных сценах.


Управление жизненным циклом

Ammo.js требует ручного освобождения памяти:

Ammo.destroy(ghostObject);
Ammo.destroy(shape);
Ammo.destroy(transform);

При утечках:

  • возрастает потребление памяти WebAssembly;
  • снижается производительность.

Частые ошибки

1. Отсутствие CF_NO_CONTACT_RESPONSE

Без флага объект начинает вести себя как обычный collision object и может мешать физике.

2. Неправильная collisionMask

Если маска не совпадает с группой других тел, пересечения не будут обнаружены.

3. Отсутствие обновления transform

Ghost-объект не интегрируется автоматически. При ручном перемещении необходимо обновлять:

ghostObject.setWorldTransform(newTransform);

Продвинутые техники

Проверка направления входа

Можно сравнивать текущий список пересечений с предыдущим кадром и определять:

  • новые контакты;
  • завершённые контакты.

Комбинирование с raycast

Ghost-объект определяет нахождение внутри зоны, а raycast уточняет:

  • расстояние до поверхности;
  • угол наклона;
  • тип объекта.

Временные сенсоры

Создание ghost-объекта на один кадр позволяет реализовать:

  • AoE-атаки;
  • проверку пространства для спавна;
  • поиск ближайших целей.

Взаимодействие с broadphase

Ghost-объекты используют тот же broadphase, что и остальные тела. Чаще всего применяется:

  • btDbvtBroadphase

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


Особенности интеграции в WebGL-сцену

Ghost-объекты:

  • могут не иметь визуального представления;
  • часто привязываются к визуальному объекту;
  • требуют синхронизации transform с графическим движком.

Порядок обновления:

  1. Обновление логики перемещения.
  2. Обновление transform ghost-объекта.
  3. Шаг физики.
  4. Чтение пересечений.
  5. Обновление визуальных эффектов.

Внутренняя модель работы

  1. Объект регистрируется в broadphase.
  2. При пересечении AABB создаётся потенциальная пара.
  3. Узкофазная проверка подтверждает пересечение.
  4. Пара добавляется в overlapping list.
  5. Ghost-объект сохраняет ссылку на другой btCollisionObject.

В btPairCachingGhostObject шаг 4 сопровождается записью в pair cache.


Ghost-объекты являются фундаментальным инструментом при построении игровых механик, где требуется обнаружение пересечений без физического взаимодействия. Правильное использование btGhostObject и btPairCachingGhostObject позволяет создавать триггерные зоны, системы персонажей и сложную логику взаимодействия при сохранении высокой производительности физической сцены.