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

Phaser 3 предлагает несколько способов объявления сцен — ключевых контейнеров для игровой логики. Использование синтаксиса класса, унаследованного от `Phaser.Scene`, — это проверенный временем подход, знакомый разработчикам с опытом в объектно-ориентированном программировании. Он обеспечивает чёткую структуру, явное наследование и полный контроль над жизненным циклом сцены, что особенно полезно для поддержки и масштабирования крупных проектов.

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

Живой запуск

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

Исходный код


const MyScene = new Phaser.Class({

    Extends: Phaser.Scene,

    initialize:

    function MyScene (config)
    {
        Phaser.Scene.call(this, config)
    },

    preload: function ()
    {
        this.load.image('face', 'assets/pics/bw-face.png');
    },

    create: function ()
    {
        this.face = this.add.image(400, 300, 'face');
    }

});

const config = {
    type: Phaser.WEBGL,
    width: 800,
    height: 600,
    backgroundColor: '#000000',
    parent: 'phaser-example',
    scene: MyScene
};

const game = new Phaser.Game(config);

Создание класса сцены

В этом подходе мы не используем ключевое слово class из ES6, а полагаемся на внутреннюю систему классов Phaser — Phaser.Class. Это позволяет создавать конструкторы сцен с явным указанием наследования.

Сначала мы определяем новый класс MyScene. Ключевой момент — указание, что он расширяет (наследует) базовый класс Phaser.Scene. Это даёт нашей сцене доступ ко всем методам движка, таким как preload, create и update.

const MyScene = new Phaser.Class({
    Extends: Phaser.Scene,

Далее идёт функция initialize, которая выступает в роли конструктора. Здесь мы вызываем конструктор родительского класса (Phaser.Scene), передавая ему конфигурацию.

initialize:
    function MyScene (config)
    {
        Phaser.Scene.call(this, config);
    },

Жизненный цикл: методы preload и create

После конструктора мы определяем методы жизненного цикла сцены. Метод preload запускается самым первым и предназначен для загрузки всех необходимых ассетов (изображений, звуков, данных) в кэш.

preload: function ()
    {
        this.load.image('face', 'assets/pics/bw-face.png');
    },

Здесь мы используем менеджер загрузки this.load, доступный в контексте сцены. Метод .image('face', ...) загружает картинку и присваивает ей ключ 'face' для последующего использования.

Метод create вызывается один раз после успешной загрузки всех ресурсов из preload. Здесь происходит начальная настройка игрового мира: создание объектов, настройка физики, ввод данных.

create: function ()
    {
        this.face = this.add.image(400, 300, 'face');
    }
Строка `this.face = this.add.image(400, 300, 'face');` делает следующее:
1.  `this.add.image` — фабричный метод для создания объекта изображения.
2.  `400, 300` — координаты X и Y для размещения центра изображения на холсте.
3.  `'face'` — ключ изображения, загруженного ранее в `preload`.
4.  `this.face = ...` — ссылка на созданный объект сохраняется в свойстве сцены, чтобы к нему можно было обратиться позже (например, для анимации).

Конфигурация игры и запуск

Отдельно от класса сцены определяется конфигурационный объект игры. В нём задаются основные параметры рендерера и, что самое важное, указывается, какой класс сцены будет запущен при старте.

const config = {
    type: Phaser.WEBGL,
    width: 800,
    height: 600,
    backgroundColor: '#000000',
    parent: 'phaser-example',
    scene: MyScene // Указываем наш класс сцены
};

Ключевое свойство scene принимает не экземпляр, а сам класс (MyScene). Phaser сам создаст экземпляр этого класса при инициализации игры.

Финальный шаг — создание экземпляра игры с переданной конфигурацией. Этот вызов запускает весь движок.

const game = new Phaser.Game(config);

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

Использование Phaser.Class для создания сцен — это фундаментальный и структурированный подход. Он идеально подходит для разработчиков, предпочитающих чёткую иерархию и ООП-архитектуру. Для экспериментов попробуйте: 1. Добавить в класс метод update и заставить изображение двигаться, изменяя свойство this.face.x. 2. Создать второй, аналогичный класс сцены (например, MenuScene) и настроить их переключение с помощью this.scene.start(). 3. Вынести общую логику (например, загрузку стандартных ассетов) в базовый класс сцены, от которого будут наследоваться MyScene и MenuScene.