О чем этот пример

В сложных играх часто требуется взаимодействие между разными игровыми сценами. Например, одна сцена может управлять логикой игры, а другая — интерфейсом или ресурсами. В этой статье разберем, как безопасно вызывать методы одной сцены из другой, используя встроенные возможности 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. Используйте этот паттерн для управления общим состоянием игры (очки, здоровье) через отдельную сервисную сцену.