О чем этот пример
В 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, и сравните подходы.
