О чем этот пример
При разработке игр часто возникает необходимость разделить логику на независимые модули. Например, фон и анимация персонажа могут быть ответственностью разных частей кода. Phaser позволяет решить эту задачу с помощью системы сцен. Использование нескольких сцен, объявленных через классы, делает код чище, повышает переиспользуемость и упрощает отладку. В этой статье мы разберем практический пример, где две сцены работают параллельно: одна отвечает за статичный фон, а другая — за вращающуюся стрелку.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Background extends Phaser.Scene
{
constructor ()
{
super({ key: 'background', active: true });
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('face', 'assets/pics/bw-face.png');
}
create ()
{
this.face = this.add.image(400, 300, 'face');
}
}
class Demo extends Phaser.Scene
{
constructor ()
{
super({ key: 'demo', active: true });
}
preload ()
{
// this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('arrow', 'assets/sprites/longarrow.png');
}
create ()
{
this.arrow = this.add.image(400, 300, 'arrow').setOrigin(0, 0.5);
}
update (time, delta)
{
this.arrow.rotation += 0.01;
}
}
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: [ Background, Demo ]
};
const game = new Phaser.Game(config);
Зачем нужны несколько сцен?
Сцена (Scene) в Phaser — это контейнер для определенной игровой логики, состоящий из методов preload, create, update и других. Когда игра становится сложнее, помещать весь код в одну сцену становится неудобно. Разделение на сцены позволяет:
* **Изолировать ответственность:** Каждая сцена отвечает за свою часть (UI, игровой мир, меню). * **Управлять состоянием:** Сцены можно запускать, приостанавливать, возобновлять и останавливать независимо друг от друга. * **Оптимизировать загрузку:** Ресурсы для разных частей игры можно загружать в разные моменты времени.
В нашем примере сцена Background загружает и отображает фоновое изображение, а сцена Demo отвечает за интерактивный объект и его анимацию. Они работают одновременно, создавая целостную картину.
Объявление сцен через классы
Современный и рекомендуемый способ создания сцен в Phaser — использование классов ES6, унаследованных от Phaser.Scene. Это делает код объектно-ориентированным и понятным.
Ключевой момент — вызов super() в конструкторе. В него передается объект конфигурации сцены. Свойство key — это уникальный строковый идентификатор сцены, а active: true означает, что сцена будет запущена автоматически при старте игры.
class Background extends Phaser.Scene
{
constructor ()
{
super({ key: 'background', active: true });
}
Таким же образом объявляется и класс Demo. Оба класса становятся независимыми модулями с собственным жизненным циклом.
Загрузка ресурсов в разных сценах
Каждая сцена может иметь свой метод preload для загрузки необходимых ей ресурсов. Это позволяет распределить загрузку по времени и логическим группам.
Обрати внимание, что в сцене Demo строка с setBaseURL закомментирована. Это потому, что базовый URL уже был установлен в сцене Background. В Phaser загрузчик (this.load) является глобальным для всего экземпляра игры, поэтому если одна сцена установила setBaseURL, последующие загрузки в других сценах могут использовать относительные пути от этого адреса. Однако для надежности лучше явно указывать настройки загрузчика в каждой сцене, если они отличаются.
// В сцене Background
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('face', 'assets/pics/bw-face.png');
}
// В сцене Demo
preload ()
{
// Базовый URL уже установлен в Background
this.load.image('arrow', 'assets/sprites/longarrow.png');
}
Создание объектов и анимация
Метод create в каждой сцене вызывается один раз после успешной загрузки ее ресурсов. Здесь создаются игровые объекты.
* В Background создается статичное изображение (this.add.image).
* В Demo создается изображение стрелки, и сразу же для него методом setOrigin(0, 0.5) смещается точка вращения. Параметры (0, 0.5) означают, что точка вращения будет на левом краю изображения по центру по вертикали. Это нужно для того, чтобы стрелка вращалась вокруг своего конца, а не центра.
// В Demo.create()
this.arrow = this.add.image(400, 300, 'arrow').setOrigin(0, 0.5);
Анимация реализуется в методе update, который вызывается на каждом кадре игры. Здесь мы увеличиваем угол поворота (rotation) стрелки на небольшую величину, зависящую от времени (delta можно использовать для плавной анимации, не привязанной к частоте кадров).
update (time, delta)
{
this.arrow.rotation += 0.01;
}
Инициализация игры и регистрация сцен
Все объявленные классы-сцены необходимо передать в главную конфигурацию игры. Это делается в массиве scene конфигурационного объекта, который затем передается в конструктор Phaser.Game.
Порядок сцен в массиве scene имеет значение для порядка их вызова методов init, preload, create, но в нашем случае, так как обе сцены active: true, они будут обновляться (update) параллельно. Важно, что сцена, указанная первой, первой и загрузит свои ресурсы.
const config = {
type: Phaser.WEBGL,
width: 800,
height: 600,
backgroundColor: '#000000',
parent: 'phaser-example',
scene: [ Background, Demo ] // Регистрация классов сцен
};
const game = new Phaser.Game(config);
Что попробовать дальше
Использование нескольких сцен, реализованных через классы, — это мощный паттерн в Phaser для структурирования кода игрового проекта. Он позволяет разбить игру на логические модули, каждый из которых управляет своей частью контента и логики. Для экспериментов попробуйте
- Добавить третью сцену для интерфейса (UI) с текстом
- Управлять сценами вручную, убрав
active: trueи запуская их с помощью методовthis.scene.start()иthis.scene.launch() - Организовать передачу данных между сценами через реестр данных игры (
this.registry) или пользовательские события (this.events)
