О чем этот пример
Создание динамичных игровых миров требует, чтобы объекты сталкивались друг с другом по-разному. В этом примере мы покажем, как один движущийся спрайт может взаимодействовать с двумя независимыми группами физических тел — шарами и ящиками — используя единый обработчик столкновений. Это фундаментальный паттерн для игр с множеством типов препятствий, платформ или врагов, где нужно эффективно управлять физикой.
Версия 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('mushroom', 'assets/sprites/mushroom2.png');
this.load.image('ball', 'assets/sprites/shinyball.png');
this.load.image('crate', 'assets/sprites/crate32.png');
}
create ()
{
const sprite = this.physics.add.image(400, 300, 'mushroom');
const outer = new Phaser.Geom.Rectangle(0, 0, 800, 600);
const inner = new Phaser.Geom.Rectangle(350, 250, 100, 100);
// Create a few balls
const balls = this.physics.add.group({ immovable: true });
for (let i = 0; i < 8; i++)
{
const point = Phaser.Geom.Rectangle.RandomOutside(outer, inner);
const ball = balls.create(point.x, point.y, 'ball');
this.physics.add.existing(ball);
ball.body.setImmovable();
}
// Create a few crates
const crates = this.physics.add.group({ immovable: true });
for (let i = 0; i < 8; i++)
{
const point = Phaser.Geom.Rectangle.RandomOutside(outer, inner);
const ball = crates.create(point.x, point.y, 'crate');
this.physics.add.existing(ball);
ball.body.setImmovable();
}
sprite.setVelocity(100, 200).setBounce(1, 1).setCollideWorldBounds(true).setGravityY(200);
this.physics.add.collider(sprite, [ balls, crates ]);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: {
debug: true
}
},
scene: Example
};
const game = new Phaser.Game(config);
Настройка сцены и загрузка ресурсов
Класс Example расширяет Phaser.Scene. В методе preload мы задаем базовый URL для загрузки и загружаем три текстуры: гриб, блестящий шар и ящик. Эти ассеты будут использоваться как спрайты.
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('mushroom', 'assets/sprites/mushroom2.png');
this.load.image('ball', 'assets/sprites/shinyball.png');
this.load.image('crate', 'assets/sprites/crate32.png');
Создание главного спрайта и определение зон
В методе create мы создаем главный физический спрайт — гриб — с помощью this.physics.add.image. Затем определяем две геометрические области: outer (весь экран 800x600) и inner (центральный прямоугольник 100x100). Область inner будет исключена из зоны появления объектов.
const sprite = this.physics.add.image(400, 300, 'mushroom');
const outer = new Phaser.Geom.Rectangle(0, 0, 800, 600);
const inner = new Phaser.Geom.Rectangle(350, 250, 100, 100);
Создание первой группы объектов — шаров
Создаем первую физическую группу balls. Ключевой параметр { immovable: true } означает, что тела в группе по умолчанию будут неподвижными при столкновениях. В цикле мы генерируем 8 случайных точек за пределами центрального прямоугольника с помощью Phaser.Geom.Rectangle.RandomOutside. Для каждого созданного спрайта в группе нужно явно добавить физическое тело через this.physics.add.existing и установить его как неподвижное.
const balls = this.physics.add.group({ immovable: true });
for (let i = 0; i < 8; i++)
{
const point = Phaser.Geom.Rectangle.RandomOutside(outer, inner);
const ball = balls.create(point.x, point.y, 'ball');
this.physics.add.existing(ball);
ball.body.setImmovable();
}
Создание второй группы объектов — ящиков
Аналогичным образом создаем вторую группу crates. Важно: хотя группа создана с параметром immovable: true, для каждого конкретного спрайта всё равно требуется явно вызвать this.physics.add.existing и setImmovable(), чтобы тело было корректно инициализировано в Arcade Physics. Это обеспечивает консистентность.
const crates = this.physics.add.group({ immovable: true });
for (let i = 0; i < 8; i++)
{
const point = Phaser.Geom.Rectangle.RandomOutside(outer, inner);
const ball = crates.create(point.x, point.y, 'crate');
this.physics.add.existing(ball);
ball.body.setImmovable();
}
Настройка физики главного спрайта и коллайдера
Задаем грибу начальную скорость, упругость (setBounce(1,1) для идеального отскока), включаем столкновение с границами мира и добавляем вертикальную гравитацию. Самое важное — создание коллайдера через this.physics.add.collider. В качестве второго аргумента мы передаем массив из двух групп [ balls, crates ]. Это заставляет гриб сталкиваться одновременно с объектами из обеих групп, используя один вызов.
sprite.setVelocity(100, 200).setBounce(1, 1).setCollideWorldBounds(true).setGravityY(200);
this.physics.add.collider(sprite, [ balls, crates ]);
Конфигурация игры и запуск
В конфигурации игры активируем Arcade Physics с опцией debug: true, чтобы видеть хитбоксы. Созданный экземпляр Phaser.Game запускает нашу сцену.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
parent: 'phaser-example',
physics: {
default: 'arcade',
arcade: { debug: true }
},
scene: Example
};
const game = new Phaser.Game(config);
Что попробовать дальше
Использование массива групп в collider — это мощный и производительный способ организовать сложные взаимодействия в игре. Вы можете расширить этот пример, добавив группам разные свойства (например, трение или отскок) или создав коллайдеры между самими группами. Попробуйте изменить логику появления объектов или добавить третью группу с врагами, которые тоже будут сталкиваться с игроком.
