О чем этот пример
Управлять десятками или сотнями однотипных объектов в игре — частая задача. Вручную обновлять координаты каждого спрайта в цикле неэффективно и громоздко. В этом примере мы рассмотрим, как использовать утилиты `Phaser.Actions.IncX` и `Phaser.Actions.WrapInRectangle` для лаконичного и производительного управления группой объектов. Вы научитесь создавать простые, но визуально привлекательные эффекты вроде параллакса или бесконечно прокручивающегося фона.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super();
this.donuts = [];
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('bg', 'assets/skies/grid.png');
this.load.image('donut', 'assets/sprites/donut.png');
}
create ()
{
this.add.image(400, 600, 'bg').setOrigin(0.5, 1);
this.cameras.main.setBounds(0, 0, 800, 600);
for (let i = 0; i < 16; i++)
{
const x = Phaser.Math.Between(0, 800);
const y = Phaser.Math.Between(200, 600);
this.donuts.push(this.add.image(x, y, 'donut'));
}
}
update ()
{
Phaser.Actions.IncX(this.donuts, -2, -0.5);
Phaser.Actions.WrapInRectangle(this.donuts, this.cameras.main.getBounds(), 128);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и создание объектов
В методе preload загружаются две текстуры: фон (bg) и спрайт пончика (donut). Обратите внимание, что фон устанавливается с помощью setOrigin(0.5, 1). Это означает, что точка его привязки (origin) находится посередине по горизонтали (0.5) и внизу по вертикали (1). Таким образом, изображение позиционируется относительно координат (400, 600) — то есть по центру нижней границы окна игры.
В методе create мы создаем массив this.donuts для хранения спрайтов и устанавливаем границы камеры (setBounds), которые будут использоваться позже. Затем в цикле создаются 16 пончиков в случайных позициях в пределах заданной области экрана, и каждый добавляется в массив.
create ()
{
this.add.image(400, 600, 'bg').setOrigin(0.5, 1);
this.cameras.main.setBounds(0, 0, 800, 600);
for (let i = 0; i < 16; i++)
{
const x = Phaser.Math.Between(0, 800);
const y = Phaser.Math.Between(200, 600);
this.donuts.push(this.add.image(x, y, 'donut'));
}
}
Массовое перемещение объектов с IncX
Каждый кадр (в методе update) вызывается Phaser.Actions.IncX. Этот метод принимает массив объектов и увеличивает (или уменьшает, если значение отрицательное) свойство `x` (горизонтальную координату) каждого из них.
Первым аргументом передается наш массив спрайтов this.donuts. Второй аргумент (-2) — это базовое значение, на которое сдвигается каждый объект. Третий аргумент (-0.5) — это случайная вариация. Итоговое смещение для каждого спрайта вычисляется по формуле: базовое значение + случайное число от 0 до вариации. Таким образом, пончики движутся влево со скоростью от -2 до -2.5 пикселя за кадр, создавая эффект небольшого разброса в скорости.
Phaser.Actions.IncX(this.donuts, -2, -0.5);
Бесконечная циклическая телепортация с WrapInRectangle
Если объекты просто уходят за левый край экрана, они исчезнут навсегда. Чтобы создать эффект бесконечного пространства или циклического движения, используется Phaser.Actions.WrapInRectangle.
Этот метод проверяет каждый объект в переданном массиве. Если объект полностью покидает заданный прямоугольник (в нашем случае — границы камеры this.cameras.main.getBounds()), он «телепортируется» на противоположную сторону. Второй аргумент (128) — это отступ (padding). Он расширяет зону проверки. Объект будет телепортирован только тогда, когда он удалится от границы прямоугольника больше чем на 128 пикселей. Это предотвращает резкие, заметные глазу телепортации прямо у края экрана.
Phaser.Actions.WrapInRectangle(this.donuts, this.cameras.main.getBounds(), 128);
В результате пончики, улетая влево, плавно возвращаются справа, создавая непрерывный поток объектов.
Конфигурация игры и запуск
Стандартная конфигурация игры Phaser 3. В поле scene передается класс нашей сцены Example. Это единственная сцена в данном примере. После создания экземпляра Phaser.Game с этой конфигурацией автоматически вызываются методы preload, create, а затем на каждом кадре — update.
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что попробовать дальше
Комбинация Phaser.Actions.IncX и WrapInRectangle — это мощный и производительный инструмент для создания динамических фонов, частиц или групп врагов с патрульным поведением. Для экспериментов попробуйте: заменить IncX на IncY для вертикального движения; использовать Phaser.Actions.Rotate для добавления вращения пончикам; или привязать прямоугольник телепортации не к границам камеры, а к произвольной области на карте.
