Фильтрация луча через collision mask

Библиотека Oimo.js предоставляет мощный физический движок для симуляции твердых тел в реальном времени в среде JavaScript. Одним из ключевых аспектов работы с физикой является контроль того, какие объекты могут взаимодействовать между собой. Collision mask и collision group позволяют гибко управлять фильтрацией столкновений, включая фильтрацию лучей при выполнении raycasting.


Основы collision group и collision mask

Каждое тело в Oimo.js имеет два параметра для фильтрации столкновений:

  • collisionGroup — определяет группу, к которой принадлежит тело.
  • collisionMask — задает, с какими группами это тело может сталкиваться.

Оба параметра представляют собой 32-битовые числа, где каждый бит соответствует отдельной группе. Применение побитовой логики позволяет создавать сложные схемы взаимодействия.

// Пример определения групп
const GROUP_DEFAULT = 1 << 0; // 0001
const GROUP_PLAYER  = 1 << 1; // 0010
const GROUP_ENEMY   = 1 << 2; // 0100
const GROUP_WALL    = 1 << 3; // 1000

// Пример создания тела с маской столкновений
let box = world.add({
    type: 'box',
    size: [1, 1, 1],
    pos: [0, 5, 0],
    move: true,
    collisionGroup: GROUP_PLAYER,
    collisionMask: GROUP_DEFAULT | GROUP_WALL // взаимодействует с DEFAULT и WALL
});

В этом примере тело игрока может сталкиваться только с объектами, принадлежащими к группам DEFAULT и WALL, а объекты из группы ENEMY игнорируются.


Raycasting в Oimo.js

Raycasting — процесс проверки пересечения луча с телами физического мира. В Oimo.js для этого используется метод world.rayCast(ray, closest), где:

  • ray — объект с начальной точкой origin и направлением direction.
  • closest — логическое значение, указывающее, нужно ли возвращать ближайшее пересечение (true) или все пересечения (false).

Стандартный вызов:

let origin = [0, 10, 0];
let direction = [0, -1, 0];
let ray = {origin, direction};

let hit = world.rayCast(ray, true); // ближайшее пересечение
if (hit) {
    console.log('Hit object:', hit.body);
}

Без фильтрации луч пересекает все тела, независимо от их групп и масок. Для точного контроля необходимо использовать collision mask при raycast.


Фильтрация луча через collision mask

Oimo.js позволяет задавать маску столкновений при выполнении raycasting, используя дополнительный параметр collisionMask. Это обеспечивает возможность игнорировать нежелательные группы тел:

let ray = {origin: [0, 10, 0], direction: [0, -1, 0]};
let mask = GROUP_DEFAULT | GROUP_WALL; // проверка только этих групп

let hit = world.rayCast(ray, true, mask);
if (hit) {
    console.log('Hit filtered object:', hit.body);
}

Здесь луч пересечет только тела из групп DEFAULT и WALL, полностью игнорируя тела из других групп. Использование побитовой маски позволяет комбинировать несколько групп одновременно.


Практические схемы фильтрации

1. Игровые персонажи и окружение

  • Игрок: взаимодействует с окружением, игнорирует собственных союзников.
  • Враги: взаимодействуют с игроком и окружением, но не с другими врагами.
  • Стена: взаимодействует со всеми.
let playerMask = GROUP_DEFAULT | GROUP_WALL; // взаимодействует с окружением
let enemyMask  = GROUP_PLAYER | GROUP_DEFAULT | GROUP_WALL; // взаимодействует с игроком и окружением

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

2. Слои визуализации

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

3. Разделение зон действия

  • Лазеры, триггеры и сенсоры могут использовать уникальные группы.
  • Лучи проверяют только целевые группы, обеспечивая оптимизацию и точность.

Побитовые операции для масок

Использование OR (|) и AND (&) операций позволяет создавать гибкие схемы:

  • Объединение групп: GROUP_PLAYER | GROUP_WALL
  • Проверка пересечения масок: (body.collisionGroup & rayMask) !== 0 — тело подходит для луча
  • Исключение группы: mask & ~GROUP_ENEMY — исключает врагов из проверки

Такой подход делает фильтрацию динамической и масштабируемой при увеличении числа объектов.


Оптимизация raycast с масками

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

Рекомендации по использованию

  1. Каждому типу объектов выделять уникальную группу.
  2. Для лучей и сенсоров всегда использовать маску, ограничивающую проверку только нужными группами.
  3. Проверять пересечение через побитовые операции, чтобы избежать ложных срабатываний.
  4. Для сложных сценариев использовать отдельные маски для разных типов raycast: стрельба, сенсоры, взаимодействие с окружением.

Фильтрация луча через collision mask в Oimo.js обеспечивает точный контроль над взаимодействием объектов, повышает производительность и позволяет создавать сложные физические сценарии без лишних проверок и конфликтов между объектами.