О чем этот пример
При разработке игр на Phaser часто возникает необходимость динамически добавлять сцены во время выполнения. Это полезно для загрузки уровней, меню или игровых модулей по требованию, что экономит начальные ресурсы. Однако в headless-режиме (например, для серверной логики или тестов) эта операция может привести к тихой ошибке, известной как issue 5974, когда сцена не инициализируется корректно. В этой статье разберем пример кода, который демонстрирует проблему, и объясним, как Phaser управляет сценами, чтобы вы могли избежать подводных камней в своих проектах.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class MyScene extends Phaser.Scene
{
constructor()
{
super('MyScene');
}
create ()
{
console.log('MyScene.create');
}
}
class Demo extends Phaser.Scene
{
constructor()
{
super('Demo');
}
create ()
{
console.log('Demo.create');
this.time.delayedCall(500, () => {
console.log('Add');
this.scene.add('MyScene', MyScene, true);
});
}
}
const config = {
type: Phaser.HEADLESS,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#000000',
scene: Demo
};
const game = new Phaser.Game(config);
Понимание headless-режима и структуры примера
Пример использует конфигурацию Phaser.HEADLESS, которая запускает Phaser без визуального рендеринга. Это идеально для серверных приложений, автоматизированного тестирования или обработки игровой логики. В коде определены две сцены: Demo (основная) и MyScene (добавляемая динамически).
Конфиг задает Demo как стартовую сцену:
const config = {
type: Phaser.HEADLESS,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#000000',
scene: Demo
};
После создания игры через new Phaser.Game(config) автоматически запускается Demo.create(). Обратите внимание: в headless-режиме параметры вроде width или backgroundColor игнорируются, но их наличие обязательно для валидности конфигурации.
Динамическое добавление сцены через delayedCall
В методе create сцены Demo используется таймер this.time.delayedCall для отложенного вызова. Это имитирует асинхронную операцию, например, загрузку ресурсов или ответ от сервера.
create ()
{
console.log('Demo.create');
this.time.delayedCall(500, () => {
console.log('Add');
this.scene.add('MyScene', MyScene, true);
});
}
Через 500 миллисекунд выполняется функция, которая добавляет MyScene с помощью this.scene.add. Третий аргумент true указывает, что сцена должна быть запущена немедленно, вызывая ее метод create. В консоли вы увидите логи: сначала Demo.create, затем Add, и наконец MyScene.create.
Проблема с ошибкой 5974 в headless-среде
Исходный код примера взят из бага под номером 5974 в репозитории Phaser. Суть проблемы: в headless-режиме при определенных условиях динамически добавленная сцена может не инициализировать свои системы (например, физику или ввод), если они требуются. Хотя в данном примере MyScene просто логирует сообщение, в реальных сценариях это может привести к падению или тихому отказу.
Ключевой момент: метод this.scene.add регистрирует сцену в менеджере сцен, а при передаче true запускает ее. Однако в headless-режиме некоторые плагины могут быть отключены, и сцена должна быть написана с учетом этого. Убедитесь, что динамические сцены не полагаются на визуальные или звуковые системы, которые недоступны в Phaser.HEADLESS.
Практические рекомендации для работы со сценами
Чтобы избежать ошибок, следуйте этим советам:
1. Проверяйте режим игры: если используется Phaser.HEADLESS, избегайте вызова методов, связанных с рендерингом.
2. Используйте this.scene.add для регистрации сцены, а this.scene.start для переключения на нее, если немедленный запуск не требуется.
3. Для отладки добавляйте логи в методы init, create и update сцен.
Пример безопасного добавления сцены без автозапуска:
this.scene.add('MyScene', MyScene, false);
this.scene.launch('MyScene');
Это дает больше контроля, особенно если нужно передать данные в сцену через init(data).
Что попробовать дальше
Динамическая подгрузка сцен — мощный инструмент в Phaser, но в headless-режиме требует осторожности из-за особенностей инициализации. Используйте пример из бага 5974 как тестовый случай для ваших сборок. Поэкспериментируйте: добавьте в MyScene вызов this.physics.add.image и проверьте, как поведет себя игра в headless и обычном режиме. Это поможет лучше понять, какие системы зависят от рендеринга.
