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

В Phaser 3 существует несколько способов определения сцены (Scene). Классы ES6 — самый популярный, но не единственный. В этом примере показан классический подход с использованием функции-конструктора и прототипа. Понимание этого метода полезно для работы с легаси-кодом, для глубокого погружения в устройство JavaScript или для проектов, где по каким-то причинам не используются классы. Это фундаментальный паттерн, лежащий в основе "современных" классов в JS.

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

Живой запуск

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

Исходный код


const MyGame = {};

MyGame.Boot = function ()
{
    this.face = null;
};

MyGame.Boot.prototype.constructor = MyGame.Boot;

MyGame.Boot.prototype = {

    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.CANVAS,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: MyGame.Boot
};

const game = new Phaser.Game(config);

Объявление пространства имён и конструктора

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

Затем определяется функция-конструктор MyGame.Boot. Внутри неё в свойстве this.face инициализируется значение null. Это будущая ссылка на игровой объект (спрайт).

const MyGame = {};

MyGame.Boot = function ()
{
    this.face = null;
};

Настройка прототипа с методами сцены

Здесь происходит основная магия. Свойству prototype функции-конструктора присваивается объект, содержащий методы жизненного цикла сцены Phaser: preload и create. Именно так методы добавляются ко всем экземплярам, созданным через new MyGame.Boot().

Строка MyGame.Boot.prototype.constructor = MyGame.Boot; — это важный шаг. После перезаписи всего прототипа новым объектом, свойство constructor внутри него теряется. Его явное восстановление — хорошая практика, чтобы конструктор корректно идентифицировался.

MyGame.Boot.prototype.constructor = MyGame.Boot;

MyGame.Boot.prototype = {

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

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

};

Методы жизненного цикла сцены

Методы в прототипе работают идентично методам в классе.

*   В `preload` с помощью `this.load.image` загружается одно изображение. Первый аргумент (`'face'`) — это ключ (key), по которому ресурс будет доступен в кеше. Второй — путь к файлу.
*   В `create` создаётся и выводится на экран игровой объект. `this.add.image` создаёт новый объект Image (статичное изображение) с центром в координатах (400, 300) и отрисовывает загруженную текстуру с ключом `'face'`. Ссылка на этот объект сохраняется в свойство `this.face`, объявленное ранее в конструкторе, для возможного дальнейшего доступа (например, для анимации).

Контекст (this) внутри этих методов — это экземпляр сцены, предоставляющий доступ ко всем системам Phaser, таким как this.load, this.add и this.physics.

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

Объект конфигурации (config) стандартен для Phaser 3. Ключевой момент здесь — свойство scene. В него передаётся не строка и не объект с настройками, а сама функция-конструктор MyGame.Boot. Phaser сам вызовет new для этого конструктора при создании экземпляра сцены.

Создание экземпляра Phaser.Game с этой конфигурацией запускает игровой цикл.

const config = {
    type: Phaser.CANVAS,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: MyGame.Boot // Передаём конструктор
};

const game = new Phaser.Game(config);

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

Подход с функцией-конструктором и прототипом — это валидный и полностью рабочий способ создания сцены в Phaser 3, демонстрирующий гибкость фреймворка. Для новых проектов предпочтительнее использовать синтаксис классов ES6 из-за его наглядности и удобства. Однако понимание прототипного подхода необходимо для чтения старого кода и глубокого владения JavaScript. **Идеи для экспериментов:** 1. Добавьте в прототип другие методы жизненного цикла, например update, и заставьте изображение двигаться, изменяя свойство this.face.x. 2. Создайте вторую аналогичную сцену (MyGame.MainMenu) и настройте их переключение с помощью this.scene.start. 3. Попробуйте переписать этот же пример, используя синтаксис класса class Boot extends Phaser.Scene, и сравните подходы.