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

Внутренняя система событий Phaser — мощный инструмент для организации кода и управления состоянием игры. Каждая сцена (`Scene`) имеет собственный экземпляр EventEmitter, доступный через свойство `this.events`. Это позволяет вам создавать и реагировать на пользовательские события внутри одной сцены, делая код более модульным, понятным и управляемым. В этой статье мы разберем, как подписываться на события и инициировать их, используя простой пример отображения изображения по сигналу.

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

Живой запуск

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

Исходный код


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

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('neuro', 'assets/pics/neuromancer.jpg');
    }

    create ()
    {
        //  Here is our event listener, the 'handler' function. The 'this' argument is the context.
        this.events.on('chatsubo', this.handler, this);

        //  We'll use the Scenes own EventEmitter to dispatch our event
        this.events.emit('chatsubo');
    }

    handler ()
    {
        this.add.image(400, 300, 'neuro');
    }
}

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Example
};

const game = new Phaser.Game(config);

Зачем нужны свои события в сцене?

Часто логика игры требует выполнять действия в ответ на определенные условия или в определенной последовательности. Жесткая запись вызовов функций может привести к запутанному коду. События (events) решают эту проблему, развязывая части кода. Один модуль (издатель) инициирует событие, а другой (подписчик) реагирует на него, не зная деталей реализации друг друга.

В контексте сцены Phaser это особенно полезно для: * Организации последовательности инициализации (например, загрузить ресурсы -> создать объекты -> запустить анимацию). * Реакции на внутренние изменения состояния (объект уничтожен, таймер истек, счет изменился). * Создания чистых и переиспользуемых компонентов.

Создание слушателя события: метод `on`

Перед тем как событие можно будет обработать, необходимо создать слушатель (подписчика). Это делается с помощью метода .on() у объекта this.events.

this.events.on('chatsubo', this.handler, this);
Разберем аргументы:
1.  `'chatsubo'` — строка, имя (тип) события. Это ваш собственный идентификатор.
2.  `this.handler` — ссылка на функцию, которая будет вызвана при наступлении события. В нашем примере это метод класса `handler`.
3.  `this` — контекст, который будет установлен внутри функции-обработчика. Указание `this` (текущей сцены) гарантирует, что внутри `handler()` мы сможем использовать API Phaser, например, `this.add.image`.

Инициация события: метод `emit`

Чтобы запустить все функции, подписанные на определенное событие, используется метод .emit().

this.events.emit('chatsubo');

Этот вызов мгновенно находит всех слушателей события с именем 'chatsubo' и вызывает их функции-обработчики, передавая управление в соответствующий код. В примере это приводит к немедленному вызову метода handler().

Обработчик события: выполнение логики

Функция-обработчик — это место, где выполняется полезная работа в ответ на событие. В нашем случае метод handler добавляет изображение на сцену.

handler ()
{
    this.add.image(400, 300, 'neuro');
}

Поскольку при подписке мы указали контекст (this), внутри обработчика мы имеем полный доступ к API сцены. Ключевой момент: изображение 'neuro' должно быть предварительно загружено в методе preload, иначе возникнет ошибка.

Практическое применение: от простого к полезному

Прямой вызов emit сразу после on, как в примере, демонстрирует механизм, но не показывает его силу. Реальная польза раскрывается, когда события разделены во времени или по условию.

create ()
{
    this.events.on('spawnEnemy', this.spawnHandler, this);

    // Событие будет инициировано позже, например, по таймеру
    this.time.delayedCall(2000, () => {
        this.events.emit('spawnEnemy');
    });
}

spawnHandler ()
{
    // Логика создания противника
}

Такой подход позволяет управлять игровым процессом через события: 'levelStart', 'playerHit', 'allCoinsCollected'. Код становится декларативным и легко расширяемым.

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

Собственные события сцены — это фундаментальный паттерн для создания структурированной и гибкой игры на Phaser. Они позволяют разным системам внутри сцены общаться, не создавая жестких зависимостей. **Идеи для экспериментов:** 1. Создайте событие 'resourcesLoaded', которое инициируется в конце метода preload, а в create подпишите на него создание игрового мира. 2. Добавьте к вызову emit дополнительные аргументы (например, this.events.emit('hit', 50)) и научите обработчик принимать урон как параметр. 3. Используйте this.events.once для подписки на событие, которое должно сработать только один раз (например, завершение обучения).