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

В Phaser сцены — это мощный инструмент для организации кода и разделения логики. Но что, если вам нужно создать несколько копий одной и той же сцены, каждая из которых работает независимо? Это открывает двери для таких механик, как мини-игры, разделённые экраны или динамически генерируемые уровни. В этой статье мы разберем пример, где одна сцена-контроллер порождает множество идентичных сцен-клонов, каждая со своей собственной жизнью и состоянием. Вы научитесь создавать сцены "на лету", передавать управление временными событиями и понимать, как разные экземпляры одной сцены могут работать параллельно, не мешая друг другу. Это продвинутая техника, которая значительно расширяет архитектурные возможности вашей игры.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Demo extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

    create ()
    {
        this.eye = this.add.image(Phaser.Math.Between(0, 800), Phaser.Math.Between(0, 600), 'eye');
    }

    update ()
    {
        this.eye.rotation += 0.02;
    }
}

class Controller extends Phaser.Scene
{
    constructor ()
    {
        super({ key: 'controller' });
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('eye', 'assets/pics/lance-overdose-loader-eye.png');
    }

    create ()
    {
        let clone = 0;
        this.time.addEvent({ delay: 1000, callback: function () {

            this.scene.add('demo' + clone, Demo, true);
            clone++;

        }, callbackScope: this, repeat: 99 });
    }
}

const config = {
    type: Phaser.WEBGL,
    width: 800,
    height: 600,
    parent: 'phaser-example',
    scene: [ Controller ]
};

const game = new Phaser.Game(config);

Архитектура примера: Контроллер и Демо

В примере используются две сцены, определенные как классы: Controller и Demo. Их роли принципиально разные.

Класс Controller — это загрузочная и управляющая сцена. Она указана в основном конфиге игры и запускается первой. Её задача — загрузить ресурсы и, по таймеру, создавать клоны сцены Demo.

Класс Demo — это тип сцены, экземпляры которой будут создаваться динамически. Каждый такой экземпляр — самостоятельная сцена со своими методами create и update. В этом примере Demo просто создает изображение глаза в случайной позиции и непрерывно вращает его.

Ключевой момент: в Phaser вы можете зарегистрировать и запустить несколько экземпляров сцены одного и того же класса, и они будут работать независимо.

Загрузка ресурсов и подготовка

Вся подготовка происходит в сцене Controller. В методе preload мы настраиваем базовый URL для загрузки и загружаем одно-единственное изображение eye. Этот ресурс будет доступен во всех создаваемых сценах, так как загрузка происходит на уровне игры.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('eye', 'assets/pics/lance-overdose-loader-eye.png');
}

Метод create сцены Controller не отображает ничего сам. Вместо этого он создает циклическое событие таймера, которое будет срабатывать каждую секунду.

Магия `this.scene.add()` и таймер

Сердце примера — в методе create сцены Controller. Здесь создается временное событие с помощью this.time.addEvent.

create ()
{
    let clone = 0;
    this.time.addEvent({
        delay: 1000,
        callback: function () {
            this.scene.add('demo' + clone, Demo, true);
            clone++;
        },
        callbackScope: this,
        repeat: 99
    });
}

Разберем параметры события: - delay: 1000 — интервал в миллисекундах (1 секунда). - callback — функция, которая выполняется при каждом срабатывании. - callbackScope: this — гарантирует, что внутри callback контекст this будет указывать на экземпляр сцены Controller. Это критически важно для доступа к this.scene. - repeat: 99 — количество повторений после первого выполнения. Вместе с первым вызовом будет создано 100 сцен.

Внутри callback происходит волшебство. Метод this.scene.add() делает три вещи: 1. **Регистрирует** новую сцену в менеджере сцен игры под уникальным ключом ('demo0', 'demo1', ...). 2. Указывает **класс** сцены (Demo), экземпляр которого нужно создать. 3. Флаг true немедленно **запускает** эту новую сцену, вызывая её методы create и затем update в основном игровом цикле.

Жизнь клона: Случайность и вращение

Каждый новый экземпляр сцены Demo начинает свою жизнь с вызова своего метода create. Здесь используется Phaser.Math.Between для задания случайных координат изображению в пределах игрового поля (800x600). Это гарантирует, что каждый "глаз" появится в своем уникальном месте.

create ()
{
    this.eye = this.add.image(
        Phaser.Math.Between(0, 800),
        Phaser.Math.Between(0, 600),
        'eye'
    );
}

После этого вступает в силу игровой цикл. Метод update вызывается для каждой активной сцены на каждом кадре. Здесь он увеличивает свойство rotation изображения this.eye на 0.02 радиана.

update ()
{
    this.eye.rotation += 0.02;
}

Важно понимать: this.eye — это свойство конкретного экземпляра сцены Demo. У каждой из 100 созданных сцен есть свой собственный this.eye со своими координатами и своим углом вращения. Они никак не конфликтуют между собой.

Итог работы: Что мы видим на экране?

Запустив этот код, вы увидите, как каждую секунду в случайном месте экрана появляется новое изображение глаза, которое сразу же начинает плавно вращаться. Через 100 секунд на экране будет 100 независимо вращающихся глаз, каждый в своей собственной, изолированной сцене.

Это наглядно демонстрирует несколько принципов Phaser: 1. **Динамическая регистрация сцен**: Сцены можно добавлять в любой момент времени, а не только при инициализации игры. 2. **Изоляция состояния**: Данные (позиция, угол поворота) инкапсулированы внутри каждого экземпляра сцены. 3. **Параллельное выполнение**: Все активные сцены получают вызовы update в каждом кадре, создавая иллюзию параллельной работы.

Менеджер сцен Phaser (this.scene) координирует всю эту деятельность, обеспечивая корректный жизненный цикл для каждой созданной сцены.

Что попробовать дальше

Динамическое создание сцен — мощный паттерн в Phaser для реализации сложной, модульной логики. Вы можете использовать его не только для визуальных эффектов, как в примере, но и для создания независимых уровней-арен, мини-игр в рамках основной или даже для разделения интерфейса и геймплея на абсолютно независимые сущности. **Идеи для экспериментов:** - Попробуйте передавать данные в новую сцену при её создании (третий аргумент this.scene.add()data). - Добавьте взаимодействие: сделайте так, чтобы клик по "глазу" удалял его сцену через this.scene.remove(). - Измените логику Controller: создавайте сцены не по таймеру, а в ответ на действия игрока. - Используйте разные классы сцен для создания разнородных игровых областей.