О чем этот пример
В сложных играх часто требуется взаимодействие между разными игровыми сценами. Например, одна сцена может управлять логикой игры, а другая — интерфейсом или ресурсами. В этой статье разберем, как безопасно вызывать методы одной сцены из другой, используя встроенные возможности Phaser. Этот подход помогает создавать модульную и поддерживаемую архитектуру проекта.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class SceneA extends Phaser.Scene
{
constructor ()
{
super({ key: 'sceneA' });
}
create ()
{
const sceneB = this.scene.get('sceneB');
this.input.on('pointerup', function ()
{
const x = Phaser.Math.Between(0, 800);
const y = Phaser.Math.Between(0, 600);
const frame = sceneB.getImage();
this.add.image(x, y, frame);
}, this);
this.add.text(10, 10, 'Click to get image', { font: '16px Courier', fill: '#00ff00' }).setDepth(1000);
}
}
class SceneB extends Phaser.Scene
{
constructor ()
{
super({ key: 'sceneB', active: true });
this.frames;
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.setPath('assets/sprites/');
this.load.image('amiga-cursor');
this.load.image('aqua_ball');
this.load.image('asuna_by_vali233');
this.load.image('atari130xe');
this.load.image('atari400');
}
create ()
{
this.frames = [ 'amiga-cursor', 'aqua_ball', 'asuna_by_vali233', 'atari130xe', 'atari400' ];
}
getImage ()
{
return Phaser.Math.RND.pick(this.frames);
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: [ SceneA, SceneB ]
};
const game = new Phaser.Game(config);
Получение ссылки на другую сцену
Основной механизм взаимодействия между сценами в Phaser — это менеджер сцен (SceneManager). Каждая сцена имеет доступ к менеджеру через свойство this.scene. Чтобы вызвать метод другой сцены, сначала нужно получить на неё ссылку.
const sceneB = this.scene.get('sceneB');
Вызов this.scene.get('sceneB') возвращает экземпляр сцены с ключом 'sceneB', если она была добавлена в игру. Это безопасный способ получить доступ, так как Phaser сам управляет жизненным циклом сцен.
Создание метода-провайдера в сцене-источнике
Сцена, которая предоставляет данные или функциональность, должна экспортировать их через свои публичные методы. В нашем примере SceneB загружает текстуры и предоставляет случайный ключ к одной из них.
getImage ()
{
return Phaser.Math.RND.pick(this.frames);
}
Метод getImage использует Phaser.Math.RND.pick для случайного выбора строки-ключа из массива this.frames. Важно, что этот метод просто возвращает данные, не создавая игровые объекты, что соответствует принципу единственной ответственности.
Вызов метода и создание объекта в целевой сцене
Сцена-потребитель (SceneA) использует полученную ссылку для вызова метода и затем сама создаёт игровой объект на основе результата.
this.input.on('pointerup', function ()
{
const x = Phaser.Math.Between(0, 800);
const y = Phaser.Math.Between(0, 600);
const frame = sceneB.getImage();
this.add.image(x, y, frame);
}, this);
При каждом клике мыши вызывается sceneB.getImage(), который возвращает случайный ключ текстуры. Затем this.add.image(x, y, frame) создаёт новый спрайт в SceneA. Обратите внимание на контекст this в обработчике события — он привязан к текущей сцене (SceneA).
Настройка сцен в конфигурации игры
Для работы примера обе сцены должны быть зарегистрированы в конфигурации игры. Порядок в массиве scene не критичен, так как Phaser создаёт экземпляры сцен самостоятельно.
scene: [ SceneA, SceneB ]
Ключевой момент — сцена SceneB помечена как активная с самого начала (active: true в конструкторе). Это гарантирует, что её метод preload выполнится и текстуры будут загружены до любых попыток вызова getImage.
super({ key: 'sceneB', active: true });
Что попробовать дальше
Вызов методов между сценами в Phaser — это мощный инструмент для разделения ответственности в вашей игре. Он позволяет создавать чистую архитектуру, где одна сцена управляет ресурсами (SceneB), а другая — их визуальным представлением (SceneA).
**Идеи для экспериментов:**
1. Сделайте метод getImage в SceneB параметрическим, чтобы можно было запрашивать текстуры по категориям.
2. Реализуйте обратную связь: пусть SceneA сообщает SceneB о том, какой спрайт был создан, для ведения статистики.
3. Используйте этот паттерн для управления общим состоянием игры (очки, здоровье) через отдельную сервисную сцену.
