О чем этот пример

Часто в играх требуется создавать новые игровые состояния или уровни "на лету", в ответ на действия игрока. В Phaser сцены (Scenes) — это основа организации кода. В этой статье мы разберем, как добавить новую сцену прямо во время выполнения игры и передать в нее начальные параметры. Этот подход полезен для создания модульных уровней, всплывающих окон или смены игровых режимов без предварительной загрузки всех сцен.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class MyScene extends Phaser.Scene {

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('face', 'assets/pics/bw-face.png');
    }

    create (data)
    {
        this.face = this.add.image(data.x, data.y, 'face');
    }

}

class BootScene extends Phaser.Scene {

    create ()
    {
        this.add.text(0, 0, 'Click to add new Scene');

        this.input.once('pointerdown', function () {
        
            this.scene.add('myScene', MyScene, true, { x: 400, y: 300 });

        }, this);
    }

}

var config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: BootScene
};

var game = new Phaser.Game(config);

Подготовка сцены-приемника

Сначала создадим сцену, которая будет добавлена динамически. Ключевой момент — метод create этой сцены должен принимать параметр data. Через этот объект мы передадим данные из вызывающей сцены.

В данном примере сцена MyScene загружает одно изображение и в методе create размещает его по координатам, переданным в data.x и data.y.

class MyScene extends Phaser.Scene {
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('face', 'assets/pics/bw-face.png');
    }

    create (data)
    {
        this.face = this.add.image(data.x, data.y, 'face');
    }
}

Запуск и инициирование добавления

Игра начинается со сцены BootScene. В ее методе create мы выводим текст-инструкцию и настраиваем обработчик клика мыши. Обратите внимание на использование this.input.once — событие сработает только один раз.

class BootScene extends Phaser.Scene {
    create ()
    {
        this.add.text(0, 0, 'Click to add new Scene');

        this.input.once('pointerdown', function () {
            // Код для добавления сцены будет здесь
        }, this);
    }
}

Контекст this в колбэке сохраняется благодаря третьему аргументу this в вызове this.input.once. Это важно, чтобы внутри функции-обработчика this указывал на текущую сцену (BootScene), а не на глобальный объект.

Динамическое добавление сцены

Внутри обработчика клика мы вызываем метод this.scene.add. Это мощный API менеджера сцен Phaser.

this.scene.add('myScene', MyScene, true, { x: 400, y: 300 });
Разберем аргументы:
1.  `'myScene'` — уникальный ключ (идентификатор) для новой сцены.
2.  `MyScene` — класс (конструктор) сцены, которую нужно создать.
3.  `true` — флаг, указывающий, что сцену нужно запустить сразу после добавления (автозапуск). Если передать `false`, сцена будет добавлена в менеджер, но для ее запуска потребуется отдельный вызов `this.scene.start('myScene')`.
4.  `{ x: 400, y: 300 }` — данные, которые будут переданы в метод `create` добавляемой сцены (`MyScene`). В нашем примере это объект с координатами.

Конфигурация игры и итог

Стандартная конфигурация игры указывает BootScene как стартовую сцену.

var config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: BootScene // Стартовая сцена
};

var game = new Phaser.Game(config);

В результате при клике на игровом поле из BootScene будет создана и запущена новая сцена MyScene. Изображение лица появится в координатах (400, 300), которые были переданы при добавлении. Исходная сцена BootScene продолжит работать, если ее не остановить явно.

Что попробовать дальше

Метод this.scene.add открывает путь к гибкой архитектуре игры. Вы можете создавать сцены по требованию, что экономит ресурсы при старте. Для экспериментов попробуйте

  1. Добавить несколько экземпляров одной и той же сцены с разными ключами и данными
  2. Не запускать сцену сразу (false), а запустить ее позже по таймеру или другому событию
  3. Передавать в данные не только координаты, но и, например, тип врага или сложность уровня