О чем этот пример
Создание сложных игр часто требует взаимодействия между сценами. Например, одна сцена может управлять игровой логикой, а другая — интерфейсом. Прямой вызов методов другой сцены позволяет избежать глобальных переменных и создает чистую архитектуру. В этой статье мы разберем практический пример вызова функции из одной сцены в другой, используя встроенный менеджер сцен Phaser 3.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class SceneA extends Phaser.Scene {
constructor ()
{
super('MyFirstScene');
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('asuna', 'assets/sprites/asuna_by_vali233.png');
}
create ()
{
this.input.on('pointerup', this.clickHandler, this);
this.add.text(10, 10, 'Click to get image', { font: '16px Courier', fill: '#00ff00' }).setDepth(1000);
}
clickHandler ()
{
let sceneB = this.scene.get('MySecondScene');
let position = sceneB.getPosition();
this.add.image(position.x, position.y, 'asuna');
}
}
class SceneB extends Phaser.Scene {
constructor ()
{
super('MySecondScene');
}
getPosition ()
{
let x = Phaser.Math.Between(0, 800);
let y = Phaser.Math.Between(0, 600);
return new Phaser.Math.Vector2(x, y);
}
}
let config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: [ SceneA, SceneB ]
};
let game = new Phaser.Game(config);
Зачем нужно взаимодействие между сценами
В Phaser 3 сцены (Scene) — это независимые модули с собственным жизненным циклом (preload, create, update). Но игровая логика часто требует обмена данными. Например, сцена меню должна передавать выбранный уровень в игровую сцену, или сцена HUD должна обновлять счет, рассчитанный в игровой логике.
Использование менеджера сцен (this.scene) для получения ссылки на объект другой сцены и вызова её методов — это чистый и контролируемый способ коммуникации. Он предпочтительнее глобальных переменных или событийной системы (EventEmitter) для простых синхронных задач.
Структура примера: две сцены
В нашем примере есть SceneA (ключ 'MyFirstScene') и SceneB (ключ 'MySecondScene'). Обе зарегистрированы в конфигурации игры.
SceneA загружает спрайт и обрабатывает клик. SceneB не отображает ничего, а выступает в роли сервиса, предоставляя метод getPosition() для получения случайных координат.
Код конфигурации игры, который запускает обе сцены:
let config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: [ SceneA, SceneB ]
};
let game = new Phaser.Game(config);
Получение сцены и вызов её метода
Вся магия происходит в методе clickHandler сцены SceneA. Когда игрок кликает, нам нужно получить случайную позицию от SceneB.
Сначала мы получаем ссылку на объект сцены SceneB через менеджер сцен текущей сцены, используя её ключ:
let sceneB = this.scene.get('MySecondScene');
Теперь sceneB — это экземпляр класса SceneB. Мы можем напрямую вызвать его публичный метод:
let position = sceneB.getPosition();
Метод getPosition() возвращает объект Phaser.Math.Vector2 с координатами. После получения позиции SceneA создает изображение в этом месте.
Полный код обработчика:
clickHandler ()
{
let sceneB = this.scene.get('MySecondScene');
let position = sceneB.getPosition();
this.add.image(position.x, position.y, 'asuna');
}
Метод-сервис в SceneB
SceneB в данном примере не является активной сценой с визуальным представлением. Она работает как вспомогательный модуль (сервис). Её ключевой метод генерирует случайную позицию.
Разберем метод getPosition():
getPosition ()
{
let x = Phaser.Math.Between(0, 800);
let y = Phaser.Math.Between(0, 600);
return new Phaser.Math.Vector2(x, y);
}
Здесь Phaser.Math.Between возвращает случайное целое число в заданном диапазоне (от 0 до ширины/высоты игры). Эти значения упаковываются в объект Vector2, который удобен для передачи пар координат `xиy`.
Важно, что метод публичный — он объявлен без особых префиксов, и поэтому доступен извне класса.
Важные нюансы и проверки
Прямой вызов методов между сценами прост, но требует осторожности.
1. **Существование сцены**: Метод `this.scene.get()` выбросит ошибку, если сцены с указанным ключом не существует. Убедитесь, что сцена зарегистрирована в конфиге игры (`scene: [ SceneA, SceneB ]`) и её ключ в `super('MySecondScene')` указан верно.
2. **Жизненный цикл**: Вы вызываете метод объекта сцены. Убедитесь, что к моменту вызова сцена уже была инициализирована (обычно это так, если она указана в массиве при запуске).
3. **Синхронность**: Этот подход синхронный. Если метод `getPosition()` выполнял бы долгую операцию, он бы "заморозил" игру. Для тяжелых задач рассмотрите использование событий (`this.events`) или асинхронных паттернов.
Пример безопасной проверки перед вызовом:
if (this.scene.isActive('MySecondScene')) {
let sceneB = this.scene.get('MySecondScene');
// ... вызов метода
}
Что попробовать дальше
Использование this.scene.get() для прямого вызова методов между сценами — мощный инструмент для создания модульной и чистой архитектуры в Phaser 3. Он отлично подходит для синхронного обмена данными, когда одна сцена выступает в роли поставщика услуг для другой.
**Идеи для экспериментов:**
1. Превратите SceneB в менеджер игровых данных: пусть она хранит счет игрока и предоставляет методы для его увеличения/получения.
2. Реализуйте сцену-настройки (SettingsScene), которая хранит громкость звука. Пусть игровая сцена обращается к ней, чтобы применить настройки.
3. Создайте сцену-пул (ObjectPoolScene) для управления переиспользуемыми игровыми объектами (пулями, эффектами), к которой будут обращаться другие сцены.
