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

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

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

Живой запуск

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

Исходный код


class SceneA extends Phaser.Scene {

    constructor ()
    {
        super({ key: 'sceneA' });
    }

    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');

        this.input.manager.enabled = true;

        this.input.once('pointerdown', function () {

            this.scene.start('sceneB');

        }, this);
    }

}

class SceneB extends Phaser.Scene {

    constructor ()
    {
        super({ key: 'sceneB' });
    }

    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.sprite(400, 300, 'arrow').setOrigin(0, 0.5);

        this.input.once('pointerdown', function (event) {

            this.scene.start('sceneC');

        }, this);
    }

    update ()
    {
        this.arrow.rotation += 0.01;
    }

}

class SceneC extends Phaser.Scene {

    constructor ()
    {
        super({ key: 'sceneC' });
    }

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

    create ()
    {
        this.add.sprite(Phaser.Math.Between(300, 600), 300, 'mech');

        this.input.once('pointerdown', function (event) {

            this.scene.start('sceneA');

        }, this);
    }

}

var config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#000000',
    parent: 'phaser-example',
    scene: [ SceneA, SceneB, SceneC ]
};

var game = new Phaser.Game(config);

Структура и конфигурация сцены

Каждая сцена в Phaser — это класс, расширяющий Phaser.Scene. В конструкторе мы задаем уникальный ключ сцены, по которому будем к ней обращаться. Этот ключ передается в объект конфигурации, который получает метод super().

Все сцены, которые вы хотите использовать в игре, передаются массивом в главную конфигурацию игры в поле scene. Порядок в массиве может влиять на порядок инициализации, но на переключение не влияет.

class SceneA extends Phaser.Scene {
    constructor ()
    {
        super({ key: 'sceneA' });
    }
}

var config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    parent: 'phaser-example',
    scene: [ SceneA, SceneB, SceneC ]
};

var game = new Phaser.Game(config);

Жизненный цикл: загрузка и создание объектов

У сцены есть предопределенные методы, которые Phaser вызывает автоматически. Два основных — preload() и create().

Метод preload() служит для загрузки всех необходимых ресурсов (изображений, звуков, данных) перед созданием сцены. В примере для загрузки используется метод this.load.image(). Важно отметить, что setBaseURL() вызывается только в первой сцене (SceneA), а в последующих закомментировано, чтобы не переопределять базовый путь.

Метод create() вызывается один раз, когда ресурсы загружены. Здесь происходит создание игровых объектов, спрайтов, настройка физики и ввода. Например, в SceneA создается изображение, а в SceneB — спрайт со смещенной точкой вращения (setOrigin(0, 0.5)).

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');
}

Механизм переключения: метод start

Переключение между сценами осуществляется с помощью менеджера сцен, доступного как this.scene. Ключевой метод для перехода — start(). Он останавливает текущую сцену (вызывая ее методы shutdown и sleep) и запускает целевую сцену (вызывая ее init, preload, create и т.д.).

В примере переход инициируется по однократному клику мыши. Обработчик события вешается с помощью this.input.once(), что гарантирует его однократное срабатывание.

this.input.once('pointerdown', function () {
    this.scene.start('sceneB');
}, this);

Обратите внимание на третий аргумент this в once(). Он задает контекст, в котором будет выполнена функция-обработчик. Без этого this.scene внутри функции был бы undefined.

Разные состояния сцен: статика и анимация

Пример демонстрирует, что сцены могут находиться в разных состояниях. SceneA и SceneC — статичны. После создания объектов они просто ждут клика для перехода.

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

update ()
{
    this.arrow.rotation += 0.01;
}

Когда вы вызываете this.scene.start('sceneC') из SceneB, ее цикл update() прекращает выполняться, и управление передается SceneC.

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

Прямое переключение сцен через start() — это базовый, но мощный инструмент организации кода. Он позволяет четко разделить логику разных частей игры. Для экспериментов попробуйте

  1. Передавать данные между сценами через аргументы метода start('key', data)
  2. Использовать this.scene.launch() для одновременной работы нескольких сцен (например, HUD поверх уровня)
  3. Управлять переходами не по клику, а по таймеру или выполнению игрового условия