Фильтрация результатов по группам

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

Настройка групп и масок

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

const GROUP_DEFAULT = 1;   // 0001
const GROUP_PLAYER = 2;    // 0010
const GROUP_ENEMY = 4;     // 0100
const GROUP_SCENERY = 8;   // 1000

Каждое тело получает группу при создании:

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

const bodyGroup = GROUP_PLAYER;
const bodyMask = GROUP_ENEMY | GROUP_SCENERY; // сталкивается с врагами и объектами окружения
physicsWorld.addRigidBody(body, bodyGroup, bodyMask);

Ключевой момент: bodyGroup определяет, к какой группе принадлежит тело. bodyMask определяет, с какими группами оно будет проверяться на коллизии.

Побитовая фильтрация

Система коллизий проверяет пересечение групп с помощью побитовой операции AND:

(bodyGroup & otherBodyMask) !== 0 && (otherBodyGroup & bodyMask) !== 0

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

Примеры практического применения

Игровые персонажи

  • Игрок (GROUP_PLAYER) сталкивается с врагами (GROUP_ENEMY) и с окружением (GROUP_SCENERY), но игнорирует другие игроки (GROUP_PLAYER):
physicsWorld.addRigidBody(playerBody, GROUP_PLAYER, GROUP_ENEMY | GROUP_SCENERY);
  • Враг сталкивается только с игроком и окружением:
physicsWorld.addRigidBody(enemyBody, GROUP_ENEMY, GROUP_PLAYER | GROUP_SCENERY);

Статические объекты сцены

  • Стены, пол, препятствия помещаются в GROUP_SCENERY и сталкиваются со всеми подвижными объектами, но не проверяют столкновения между собой:
physicsWorld.addRigidBody(wallBody, GROUP_SCENERY, GROUP_PLAYER | GROUP_ENEMY);

Динамическая смена групп и масок

Иногда необходимо изменить поведение тела во время симуляции, например, временно сделать объект «прозрачным» для коллизий.

body.getBroadphaseHandle().setCollisionFilterGroup(GROUP_PLAYER);
body.getBroadphaseHandle().setCollisionFilterMask(GROUP_SCENERY | GROUP_ENEMY);

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

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

Метод addRigidBody с указанием групп и масок автоматически обновляет внутренние структуры broadphase. Для существующих тел, которые меняют маску или группу, необходимо вручную обновлять broadphase, чтобы изменения вступили в силу:

physicsWorld.getBroadphase().getOverlappingPairCache().cleanProxyFromPairs(body.getBroadphaseHandle(), physicsWorld.getDispatcher());

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

Рекомендации по структуре групп

  • Использовать степени двойки для каждой уникальной группы.
  • Минимизировать количество пересекающихся групп для оптимизации производительности.
  • Разделять статические и динамические объекты, чтобы broadphase мог исключить ненужные пары сразу.
  • Для сложных сценариев, где один объект должен участвовать в нескольких типах столкновений, комбинировать маски через побитовое OR.

Отладка и визуализация

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


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