О чем этот пример
При разработке игр на Phaser важно контролировать порядок инициализации систем. Частая ошибка — обращение к методам плагина в сцене до того, как сам плагин был запущен и сопоставлен (mapped). Это приводит к фатальной ошибке `TypeError: Cannot read properties of undefined` и остановке игры. В этой статье разберем, как правильно настраивать плагины в конфигурации игры, чтобы избежать этой проблемы и гарантировать, что ваши кастомные системы будут готовы к работе в момент создания сцены.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class TestPlugin extends Phaser.Plugins.BasePlugin
{
constructor(pluginManager)
{
super(pluginManager);
}
wibble ()
{
console.log('hello');
}
}
class Example extends Phaser.Scene
{
constructor()
{
super({
key: "example"
});
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('elephant', 'assets/sprites/elephant.png');
}
create ()
{
console.log('create');
this.test.wibble();
}
}
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#1d1d1d',
parent: 'phaser-example',
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH
},
scene: [ Example ],
plugins: {
global: [
{ key: 'TestPlugin', plugin: TestPlugin, start: false, mapping: 'test' }
]
},
};
var game = new Phaser.Game(config);
Проблема: сцена создается до инициализации плагина
В предоставленном исходном коде плагин TestPlugin объявлен в глобальных плагинах конфига, но с параметром start: false. Это означает, что плагин будет зарегистрирован в Plugin Manager, но не будет автоматически запущен (т.е., его метод start не вызовется) перед созданием сцен.
Ключевой параметр mapping: 'test' указывает, что после запуска экземпляр плагина будет доступен в сцене через свойство this.test.
Ошибка возникает в методе create сцены Example, в строке:
this.test.wibble();
На момент выполнения этой строки свойство this.test равно undefined, потому что плагин с ключом 'TestPlugin' еще не был запущен (не был вызван его метод start), и, следовательно, сопоставление (mapping) не произошло. Сцена пытается использовать функциональность, которая еще не готова.
Решение: правильная конфигурация плагина
Чтобы плагин был доступен в сцене с самого начала ее жизненного цикла (в create, update и т.д.), его необходимо запустить. Делается это через параметр start в конфигурации плагина.
Исправленная конфигурация игры должна выглядеть так:
var config = {
// ... другие настройки (type, width, height и т.д.)
scene: [ Example ],
plugins: {
global: [
{ key: 'TestPlugin', plugin: TestPlugin, start: true, mapping: 'test' }
]
},
};
Установка start: true гарантирует, что плагин будет запущен (и, как следствие, сопоставлен) до создания и инициализации любой из сцен, перечисленных в массиве scene. После этого в методе create сцены Example свойство this.test будет содержать экземпляр TestPlugin, и вызов this.test.wibble() успешно выполнится, выводя 'hello' в консоль.
Как работает параметр mapping
Параметр mapping — это мощный инструмент для организации кода. Он автоматически инжектирует (внедряет) экземпляр плагина в ключевые объекты Phaser.
* При mapping: 'test' для **глобального** плагина (global:), экземпляр плагина становится доступен во всех системах, которые его поддерживают, например, в каждой сцене (Scene), через свойство с указанным именем (this.test).
* Также можно использовать mapping для плагинов уровня сцены (scene:), но в этом случае плагин будет доступен только в рамках конкретной сцены, для которой он сконфигурирован.
После корректной настройки вы можете обращаться к методам плагина напрямую:
create ()
{
// this.test теперь существует и является экземпляром TestPlugin
this.test.wibble(); // Выведет 'hello' в консоль
}
Когда может понадобиться start: false?
Возникает логичный вопрос: зачем вообще нужен параметр start: false, если он вызывает проблемы? Его использование оправдано в сценариях отложенной или ручной инициализации.
1. **Динамическая загрузка плагинов:** Вы можете зарегистрировать плагин на старте игры, но запустить его позже, по требованию (например, после загрузки каких-то ассетов или в ответ на действие игрока). 2. **Условный запуск:** Запуск плагина только на определенных уровнях или в определенных игровых режимах.
Для ручного запуска плагина, сконфигурированного с start: false, можно использовать Plugin Manager:
// Где-то в коде вашей сцены, например, после условия
this.plugins.start('TestPlugin');
// Теперь mapping сработает, и this.test станет доступен
if (this.test) {
this.test.wibble();
}
Важно помнить, что mapping срабатывает именно в момент **запуска** (start) плагина, а не его регистрации.
Что попробовать дальше
Порядок инициализации в Phaser имеет значение. Всегда проверяйте, что плагины, от которых зависит ваша сцена, запущены (start: true) до её создания. Используйте start: false осознанно, для сценариев отложенной инициализации, и не забывайте вручную запускать плагин через this.plugins.start() перед обращением к его методам.
**Идеи для экспериментов:**
1. Создайте плагин, который зависит от загруженного ассета (например, шрифта). Настройте его с start: false и запускайте только после события filecomplete в preload сцены.
2. Реализуйте систему «тяжелых» плагинов (например, для аналитики), которые должны запускаться не сразу при старте игры, а только после первого клика пользователя (согласие на сбор данных).
3. Попробуйте использовать плагины уровня сцены (scene: в конфиге) с маппингом и сравните их область видимости с глобальными.
