О чем этот пример
При разработке игр часто возникает ситуация, когда разные типы объектов должны сталкиваться только с определёнными «слоями» игрового мира. Например, пули игрока должны игнорировать других игроков, но поражать врагов. Механизм категорий столкновений в Arcade Physics позволяет гибко настраивать такие правила без написания сложных условий вручную. В этой статье мы разберём, как использовать `setCollisionCategory` и `setCollidesWith` для создания избирательной физики, что сделает вашу игру более управляемой и производительной.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('blockA', 'assets/sprites/blockA.png');
this.load.image('blockB', 'assets/sprites/blockB.png');
this.load.image('blockC', 'assets/sprites/blockC.png');
this.load.image('blockD', 'assets/sprites/blockD.png');
}
create ()
{
const blockA = this.physics.add.sprite(200, 300, 'blockA');
const blockB = this.physics.add.sprite(400, 300, 'blockB');
const blockC = this.physics.add.sprite(600, 300, 'blockC');
const category1 = this.physics.nextCategory();
// blockA.setCollisionCategory(category1);
// blockB.setCollisionCategory(category1);
// blockA.setCollidesWith(category1);
// blockB.setCollidesWith(category1);
const blocks = [ blockB, blockC ];
this.physics.add.collider(blockA, blocks);
this.input.once('pointerdown', () => {
blockA.setVelocityX(200);
});
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
gravity: { y: 0 },
debug: true
}
},
scene: Example
};
const game = new Phaser.Game(config);
Суть категорий столкновений
Движок Arcade Physics позволяет назначать физическим телам битовые маски — категории. Категория определяет принадлежность объекта к определённой группе. Затем можно задать, с какими другими категориями это тело может сталкиваться.
Это работает как фильтр: если категории двух объектов не совпадают согласно заданным правилам, движок проигнорирует их столкновение, даже если они физически перекрываются. Это фундаментально для создания сложных взаимодействий, таких как сквозные платформы, игнорирование союзников или разные слои ловушек.
const category1 = this.physics.nextCategory();
Настройка категорий и правил
Метод this.physics.nextCategory() возвращает уникальную битовую маску для новой категории. Чтобы назначить её объекту, используется метод setCollisionCategory(). Правила взаимодействия задаются методом setCollidesWith(), который принимает одну или несколько категорий (через побитовое ИЛИ).
В исходном примере код, который настраивает категории, закомментирован. Давайте его раскомментируем и посмотрим, что произойдёт.
// Назначаем blockA и blockB одну и ту же категорию
blockA.setCollisionCategory(category1);
blockB.setCollisionCategory(category1);
// Говорим, что blockA и blockB сталкиваются ТОЛЬКО с объектами своей категории (category1)
blockA.setCollidesWith(category1);
blockB.setCollidesWith(category1);
Анализ работы примера
В функции create создаются три спрайта с физикой: blockA, blockB и blockC. Создаётся один коллайдер между blockA и массивом [blockB, blockC]. По клику blockA получает скорость.
**Без категорий (исходное состояние):** Все три блока будут сталкиваться друг с другом, так как коллайдер регистрирует пары (blockA-blockB) и (blockA-blockC).
**С раскомментированными категориями:** blockA и blockB принадлежат category1 и настроены сталкиваться только с ней. blockC категории не назначено.
1. blockA и blockB будут сталкиваться (общая категория).
2. blockA и blockC НЕ будут сталкиваться, так как blockA «видит» только category1, а у blockC её нет.
3. blockB и blockC также не столкнутся, по той же причине.
Коллайдер между blockA и массивом [blockB, blockC] всё равно существует, но фильтр категорий отменит столкновение с blockC на уровне движка.
// Коллайдер создаётся для всех пар, но фильтры могут его отменить
this.physics.add.collider(blockA, blocks); // blocks = [blockB, blockC]
Практическое применение и отладка
Включённый режим отладки (debug: true) визуализирует физические тела. Объекты, которые не сталкиваются из-за фильтров, всё равно будут иметь контуры, но при столкновении не будет происходить отталкивания.
Этот механизм идеален для: * **Разных типов снарядов:** пули игрока не задевают его самого. * **Слоёв локации:** декорации, сквозь которые можно пройти. * **Командных взаимодействий:** игроки одной команды не блокируют друг друга.
Важно помнить, что категория — это битовая маска. Метод nextCategory() автоматически генерирует последовательные степени двойки (1, 2, 4, 8...), что позволяет комбинировать их через побитовое ИЛИ.
// Конфигурация физики с включённой отладкой
physics: {
default: 'arcade',
arcade: {
gravity: { y: 0 },
debug: true // Показывает хитбоксы
}
}
Что попробовать дальше
Фильтрация столкновений через категории — мощный и эффективный инструмент Arcade Physics. Он перекладывает логику «кто с кем сталкивается» с уровня скриптов на уровень конфигурации физического движка, что повышает чистоту кода и производительность. Для экспериментов попробуйте создать несколько категорий (category1, category2) и назначить объектам сложные правила, например, blockA.setCollidesWith(category1 | category2). Или сделайте объект-призрак, который не сталкивается ни с чем, кроме одной особой категории.
