В игровой физике контроль столкновений между объектами является
ключевым аспектом для оптимизации и правильного поведения сцены. В
библиотеке Phaser 3 с использованием движка
Matter.js предоставляется мощный механизм фильтрации
столкновений через свойства collisionFilter, которые
включают category и mask.
Каждое тело в Matter.js имеет объект collisionFilter,
содержащий три поля:
category – уникальная категория, которой
принадлежит тело. Представляется как битовая маска (значение типа
uint16), обычно в виде степени двойки:
body.collisionFilter.category = 0x0001; // категория 1mask – маска, определяющая, с какими категориями тело может сталкиваться. Тело сталкивается только с объектами, категория которых присутствует в его маске:
body.collisionFilter.mask = 0x0002 | 0x0004; // столкновения с категориями 2 и 3group – позволяет создавать группы столкновений с более сложной логикой (необязательное поле).
Фильтрация работает на битовом уровне: проверяется
логическое И между category одного тела и mask
другого, и наоборот. Только если обе проверки возвращают истину,
происходит столкновение.
Для удобства обычно создают константы:
const CATEGORY_PLAYER = 0x0001;
const CATEGORY_ENEMY = 0x0002;
const CATEGORY_BULLET = 0x0004;
const CATEGORY_WALL = 0x0008;
Каждой категории присваивается уникальная степень двойки. Это
обеспечивает возможность комбинировать их через побитовое ИЛИ
(|) для масок.
Пример конфигурации игрока, врагов и пуль:
// Игрок: сталкивается с врагами и стенами
player.body.collisionFilter.category = CATEGORY_PLAYER;
player.body.collisionFilter.mask = CATEGORY_ENEMY | CATEGORY_WALL;
// Враг: сталкивается с игроком и стенами, но не с пулями других врагов
enemy.body.collisionFilter.category = CATEGORY_ENEMY;
enemy.body.collisionFilter.mask = CATEGORY_PLAYER | CATEGORY_WALL;
// Пуля игрока: сталкивается только с врагами и стенами
bullet.body.collisionFilter.category = CATEGORY_BULLET;
bullet.body.collisionFilter.mask = CATEGORY_ENEMY | CATEGORY_WALL;
В данном примере пули не сталкиваются друг с другом,
поскольку их категория CATEGORY_BULLET отсутствует в маске
другой пули.
Маски позволяют создавать сложные правила столкновений:
Для добавления нескольких категорий используется побитовое ИЛИ:
body.collisionFilter.mask = CATEGORY_PLAYER | CATEGORY_ENEMY | CATEGORY_WALL;
Для исключения категории используют побитовое НЕ и побитовое И:
body.collisionFilter.mask = ~CATEGORY_BULLET; // сталкивается со всеми, кроме пуль
Поле group позволяет объединять объекты в группы с более детальной фильтрацией:
Пример:
player.body.collisionFilter.group = 1; // Игрок и союзники не сталкиваются между собой
ally.body.collisionFilter.group = 1;
enemy.body.collisionFilter.group = -1; // Враги всегда сталкиваются друг с другом
Группы часто применяются для управления союзниками, стаями NPC или частицами.
Всегда использовать степени двойки для
category и масок, чтобы фильтрация работала
корректно.
Минимизировать количество категорий — слишком большое число категорий и масок может усложнить отладку.
Разделять объекты по логике столкновений — игровые элементы, проектiles, окружение.
Проверять маски через битовую арифметику при отладке:
console.log((bodyA.collisionFilter.category & bodyB.collisionFilter.mask) !== 0 &&
(bodyB.collisionFilter.category & bodyA.collisionFilter.mask) !== 0);ghost, но
сталкивается с стенами:const CATEGORY_GHOST = 0x0010;
ghost.body.collisionFilter.category = CATEGORY_GHOST;
ghost.body.collisionFilter.mask = 0x0000; // не сталкивается ни с кем
player.body.collisionFilter.mask |= CATEGORY_WALL; // игрок сталкивается со стенами
const CATEGORY_BULLET_PLAYER = 0x0004;
const CATEGORY_BULLET_ENEMY = 0x0010;
bulletPlayer.body.collisionFilter.category = CATEGORY_BULLET_PLAYER;
bulletPlayer.body.collisionFilter.mask = CATEGORY_ENEMY | CATEGORY_WALL;
bulletEnemy.body.collisionFilter.category = CATEGORY_BULLET_ENEMY;
bulletEnemy.body.collisionFilter.mask = CATEGORY_PLAYER | CATEGORY_WALL;
Фильтрация столкновений через category и
mask позволяет создавать гибкую и эффективную систему
взаимодействий между объектами. Это особенно важно для больших проектов
с множеством игровых сущностей, где классический метод проверки
столкновений «каждый со всеми» становится неоптимальным.