Cannon.js — это физический движок для JavaScript, обеспечивающий реалистичное моделирование твердых тел, столкновений и взаимодействий в 3D-пространстве. Одним из ключевых инструментов для оптимизации работы с физикой является рейкаст (raycast) — проверка пересечения луча с объектами сцены. В сочетании с коллизионными масками рейкаст становится гибким инструментом фильтрации объектов.
Рейкаст в Cannon.js создаётся с помощью объекта
CANNON.Ray. Основные параметры:
from — начальная точка луча (CANNON.Vec3).to — конечная точка луча (CANNON.Vec3).collisionFilterMask — маска для фильтрации объектов, с
которыми может пересекаться луч.collisionFilterGroup — группа самого луча для
взаимодействия с объектами сцены.Пример базового создания луча:
const ray = new CANNON.Ray(from, to);
ray.intersectWorld(world, {
collisionFilterMask: 1,
collisionFilterGroup: 1,
skipBackfaces: true
});
Параметры collisionFilterMask и
collisionFilterGroup определяют, с какими объектами луч
будет проверять столкновения.
Каждое тело в Cannon.js имеет два свойства:
collisionFilterGroup — указывает группу, к которой
принадлежит объект.collisionFilterMask — указывает группы, с которыми
объект может сталкиваться.Оба свойства — целые числа, где каждая единица бита обозначает отдельную группу. Это позволяет создавать сложные фильтры для столкновений.
Пример назначения группы и маски:
const sphere = new CANNON.Body({
mass: 5,
shape: new CANNON.Sphere(1),
collisionFilterGroup: 2, // принадлежит группе 2
collisionFilterMask: 1 | 4 // сталкивается с группами 1 и 4
});
В этом примере сфера может взаимодействовать только с объектами из групп 1 и 4, игнорируя все остальные.
При использовании ray.intersectWorld можно задать маску
через объект options:
ray.intersectWorld(world, {
collisionFilterMask: 1 | 2, // проверка только объектов из групп 1 и 2
collisionFilterGroup: 8, // луч принадлежит группе 8
skipBackfaces: false
});
Если луч принадлежит группе, которая не указана в маске объекта, столкновение не будет засчитано. Это позволяет:
Предположим, что есть игрок и враги, каждый с собственными коллизионными группами:
const PLAYER_GROUP = 1;
const ENEMY_GROUP = 2;
const ENV_GROUP = 4;
// тело врага
const enemy = new CANNON.Body({
mass: 0,
shape: new CANNON.Box(new CANNON.Vec3(1,1,1)),
collisionFilterGroup: ENEMY_GROUP,
collisionFilterMask: PLAYER_GROUP
});
// тело стены
const wall = new CANNON.Body({
mass: 0,
shape: new CANNON.Box(new CANNON.Vec3(5,5,1)),
collisionFilterGroup: ENV_GROUP,
collisionFilterMask: PLAYER_GROUP | ENEMY_GROUP
});
// создание луча для проверки попадания игрока во врагов
const ray = new CANNON.Ray(player.position, enemy.position);
ray.intersectWorld(world, {
collisionFilterMask: ENEMY_GROUP, // игнорируем стены
collisionFilterGroup: PLAYER_GROUP
});
В результате луч проверит пересечение только с телами врагов, игнорируя объекты окружающей среды.
1 << n. Это позволяет
гибко комбинировать группы.true,
луч не учитывает обратные стороны треугольников, что полезно для
визуальных эффектов и оптимизации.Body и
World: рейкасты работают только с телами, которые
находятся в world.bodies и имеют физические формы
(shape).intersectWorld позволяет получать массив всех пересечений с
объектами, удовлетворяющими маске.Пример получения всех попаданий:
const hits = [];
ray.intersectWorld(world, {
collisionFilterMask: ENEMY_GROUP,
collisionFilterGroup: PLAYER_GROUP,
mode: CANNON.Ray.ALL,
result: hits
});
console.log(hits.length); // количество объектов, с которыми пересёкся луч
Коллизионные маски при рейкасте в Cannon.js позволяют создавать
точные, оптимизированные проверки пересечений. Правильное использование
collisionFilterMask и collisionFilterGroup
обеспечивает эффективное управление столкновениями, разделение
функционала лучей и снижение вычислительной нагрузки в больших сценах с
множеством объектов.
Хотите, я могу подготовить подробную схему визуализации взаимодействия групп и масок для рейкаста, чтобы было проще понять битовую логику?