О чем этот пример
При создании игр часто возникает необходимость ограничить движение объектов определённой зоной, но не просто остановить их, а «зациклить» пространство, чтобы они появлялись с противоположной стороны. Это классический приём для космических симуляторов, экранов радара или мини-карт. В этой статье разберём пример из официальной документации Phaser, который показывает, как с помощью `Phaser.Actions.WrapInRectangle` легко реализовать такое поведение для десятков спрайтов одновременно, не управляя каждым вручную.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super();
this.wrapRect;
this.aliens = [];
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('monitor', 'assets/pics/monitor.png');
this.load.image('alien', 'assets/sprites/space-baddie.png');
}
create ()
{
// This is our 'wrapping rectangle'
// When a sprite leaves this, it'll be wrapped around
this.wrapRect = new Phaser.Geom.Rectangle(214, 132, 367, 239);
this.add.rectangle(this.wrapRect.x, this.wrapRect.y, this.wrapRect.width, this.wrapRect.height, 0x0094bf).setOrigin(0, 0);
for (let i = 0; i < 64; i++)
{
const x = Phaser.Math.Between(this.wrapRect.left, this.wrapRect.right);
const y = Phaser.Math.Between(this.wrapRect.top, this.wrapRect.bottom);
this.aliens.push(this.add.image(x, y, 'alien'));
}
this.add.image(400, 300, 'monitor');
}
update ()
{
// Move all the sprites
Phaser.Actions.IncXY(this.aliens, 1.5, 2.5, 0.04);
// Wrap the sprites within our wrapping rectangle, with an 8px buffer
Phaser.Actions.WrapInRectangle(this.aliens, this.wrapRect, 8);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000042',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и создание зоны
Вся логика примера построена вокруг одного прямоугольника (Phaser.Geom.Rectangle), который задаёт границы для «заворачивания» спрайтов. Сначала мы создаём этот прямоугольник в методе create() сцены.
this.wrapRect = new Phaser.Geom.Rectangle(214, 132, 367, 239);
Здесь заданы координаты левого верхнего угла (214, 132), ширина (367) и высота (239). Для наглядности на сцену добавляется графический прямоугольник с таким же размером и полупрозрачной заливкой.
this.add.rectangle(this.wrapRect.x, this.wrapRect.y, this.wrapRect.width, this.wrapRect.height, 0x0094bf).setOrigin(0, 0);
Метод .setOrigin(0, 0) устанавливает точку привязки (origin) в левый верхний угол, чтобы визуальный прямоугольник точно совпал с логической зоной wrapRect.
Создание и расстановка спрайтов
В примере создаётся 64 спрайта пришельцев ('alien'). Их начальные позиции генерируются случайным образом, но строго внутри границ нашего прямоугольника wrapRect. Для этого используется Phaser.Math.Between, которая возвращает случайное целое число в заданном диапазоне.
const x = Phaser.Math.Between(this.wrapRect.left, this.wrapRect.right);
const y = Phaser.Math.Between(this.wrapRect.top, this.wrapRect.bottom);
this.aliens.push(this.add.image(x, y, 'alien'));
Все созданные спрайты сохраняются в массив this.aliens. Это ключевой момент, так как Phaser.Actions работают именно с массивами игровых объектов. После этого на задний план добавляется статичное изображение монитора для создания контекста.
Движение объектов с помощью Phaser.Actions.IncXY
В методе update(), который вызывается каждый кадр, необходимо двигать все спрайты. Вместо перебора массива в цикле, используется удобный хелпер Phaser.Actions.IncXY. Он применяет одно и то же смещение по осям X и Y ко всем объектам в переданном массиве.
Phaser.Actions.IncXY(this.aliens, 1.5, 2.5, 0.04);
Параметры функции:
- this.aliens — массив спрайтов.
- 1.5 — смещение по оси X за кадр.
- 2.5 — смещение по оси Y за кадр.
- 0.04 — *шаг* (step). Это необязательный параметр, который добавляет случайное отклонение к смещению, делая движение более естественным. Значение 0.04 означает ±4% от базового смещения.
Магия зацикливания: WrapInRectangle
Сердце примера — метод Phaser.Actions.WrapInRectangle. Он проверяет положение каждого спрайта в массиве относительно заданного прямоугольника и, если спрайт его покинул, «перебрасывает» его на противоположную сторону.
Phaser.Actions.WrapInRectangle(this.aliens, this.wrapRect, 8);
Параметры функции:
- `this.aliens` — массив спрайтов для обработки.
- `this.wrapRect` — прямоугольник-граница.
- `8` — *буфер* (padding). Это важный параметр. Он определяет, насколько спрайт должен выйти за границу прямоугольника, прежде чем сработает «заворачивание». Без буфера спрайт начинает мигать на самой границе, так как в одном кадре он вышел, его вернули, а в следующем он снова вышел. Буфер в 8 пикселей решает эту проблему.
Логика работы: если координата спрайта по X меньше wrapRect.left - 8, она устанавливается в wrapRect.right. Если больше wrapRect.right + 8 — в wrapRect.left. Аналогично для координаты Y.
Конфигурация игры и запуск
Код завершается стандартной конфигурацией игры Phaser 3 и её инстанцированием. Обратите внимание на фоновый цвет (backgroundColor), который создаёт эффект космоса.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000042',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Ключевой момент: основная сцена (scene) указана как класс Example, который мы и разбирали. Вся логика «заворачивания» инкапсулирована внутри методов этого класса.
Что попробовать дальше
Phaser.Actions.WrapInRectangle — это мощный и производительный инструмент для управления группами объектов в ограниченном пространстве. Он избавляет от необходимости писать собственные проверки границ и циклы.
**Идеи для экспериментов:**
1. Измените параметры IncXY и буфер WrapInRectangle, чтобы увидеть, как это влияет на плавность и поведение системы.
2. Попробуйте применить WrapInRectangle не ко всем, а к отдельной группе спрайтов, создав второй массив.
3. Вместо прямоугольника используйте Phaser.Actions.WrapInCircle для создания круговой или орбитальной зоны.
