Битовые маски: collisionGroup и collisionMask

В физическом движке Oimo.js управление столкновениями между объектами осуществляется с помощью битовых масок. Основные инструменты для этого — свойства collisionGroup и collisionMask. Они позволяют задать, какие объекты будут взаимодействовать, а какие — игнорироваться при расчёте коллизий.


Принцип работы битовых масок

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

Например:

const group1 = 1 << 0; // 0001
const group2 = 1 << 1; // 0010
const group3 = 1 << 2; // 0100

Аналогично, collisionMask задаёт с какими группами объект может сталкиваться.

Пример логики:

  • collisionGroup = group1
  • collisionMask = group2 | group3

Это означает, что объект из группы 1 будет взаимодействовать только с объектами, принадлежащими к группам 2 и 3.


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

Каждое тело можно создать с указанием collisionGroup и collisionMask через свойства формы (shape) или напрямую через тело:

const boxShape = new OIMO.BoxGeometry(new OIMO.Vec3(1, 1, 1));
const box = new OIMO.RigidBody({
    type: 'dynamic',
    position: new OIMO.Vec3(0, 5, 0),
    shapes: [boxShape]
});

box.shapes[0].collisionGroup = 1 << 0; // группа 1
box.shapes[0].collisionMask = (1 << 1) | (1 << 2); // столкновения с группами 2 и 3

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


Проверка столкновений

Oimo.js проверяет столкновения через побитовое сравнение:

collisionA = bodyA.collisionGroup & bodyB.collisionMask
collisionB = bodyB.collisionGroup & bodyA.collisionMask

Если результат не равен нулю в обоих направлениях, объекты будут взаимодействовать.

Пример:

const aGroup = 1 << 0;  // 0001
const bGroup = 1 << 1;  // 0010

// объект A
bodyA.shapes[0].collisionGroup = aGroup;
bodyA.shapes[0].collisionMask = bGroup;

// объект B
bodyB.shapes[0].collisionGroup = bGroup;
bodyB.shapes[0].collisionMask = aGroup;

// Проверка:
(bodyA.shapes[0].collisionGroup & bodyB.shapes[0].collisionMask) !== 0 // true
(bodyB.shapes[0].collisionGroup & bodyA.shapes[0].collisionMask) !== 0 // true

Если хотя бы один из результатов равен нулю, столкновение игнорируется.


Практические схемы использования

  1. Игнорирование игрока и его снарядов:

    • Игрок: collisionGroup = 1 << 0, collisionMask = ~0 (все группы)
    • Снаряд игрока: collisionGroup = 1 << 1, collisionMask = ~ (1 << 0) Снаряд не будет сталкиваться с самим игроком, но будет с остальными объектами.
  2. Слои окружения:

    • Земля: group = 1 << 2, маска = все
    • Стены: group = 1 << 3, маска = все кроме снарядов игрока
  3. Комбинированные объекты: Один объект может одновременно принадлежать нескольким группам:

    body.shapes[0].collisionGroup = (1 << 0) | (1 << 2); 
    body.shapes[0].collisionMask = (1 << 1) | (1 << 3);

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


Советы по оптимизации

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

Битовые маски collisionGroup и collisionMask в Oimo.js дают полный контроль над системой столкновений и позволяют создавать сложные, гибкие сцены без необходимости вручную фильтровать каждую коллизию.