В 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());
Это гарантирует корректную фильтрацию столкновений после изменения групп.
Для сложных фильтров рекомендуется временно визуализировать группы с помощью отрисовки коллайдеров разными цветами. Это помогает понять, почему некоторые объекты игнорируют столкновения, а другие нет.
Использование групп и масок в Ammo.js позволяет
создавать гибкую и высокопроизводительную систему фильтрации
столкновений, контролируя взаимодействия между объектами без лишних
затрат на проверку каждой пары в симуляции.