О чем этот пример
Встроенная система событий Phaser — мощный инструмент для организации взаимодействия между компонентами игры. Часто бывает нужно создать собственный независимый источник событий для отдельного модуля или логики, не связанной напрямую с `Scene` или `Game`. В этой статье разберем, как создать экземпляр `Phaser.Events.EventEmitter`, настроить обработчики и генерировать события с передачей данных, что позволит сделать код чище и модульнее.
Версия 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('plush', 'assets/pics/profil-sad-plush.png');
}
create ()
{
// Create our own EventEmitter instance
var emitter = new Phaser.Events.EventEmitter();
// Set-up an event handler
emitter.on('addImage', this.handler, this);
// Emit it a few times with varying arguments
emitter.emit('addImage', 200, 300);
emitter.emit('addImage', 400, 300);
emitter.emit('addImage', 600, 300);
}
handler (x, y)
{
this.add.image(x, y, 'plush');
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Зачем нужен собственный EventEmitter?
Каждая сцена (Scene) в Phaser уже имеет свой собственный events (this.events), который отлично подходит для внутренней коммуникации. Однако, если вы разрабатываете сложную систему, например, менеджер достижений, контроллер UI или кастомную физическую модель, может потребоваться изолированный канал событий. Создание отдельного экземпляра EventEmitter предотвращает конфликты имен событий и делает зависимости между модулями более явными.
В предоставленном примере мы создаем эмиттер для управления добавлением изображений, что является простой, но наглядной демонстрацией подхода.
Создание экземпляра и подписка на событие
В методе create() сцены создается новый объект-излучатель событий. Затем на нем регистрируется обработчик для конкретного имени события с помощью метода on().
// Create our own EventEmitter instance
var emitter = new Phaser.Events.EventEmitter();
// Set-up an event handler
emitter.on('addImage', this.handler, this);
Ключевые моменты:
- new Phaser.Events.EventEmitter() — создает новый, независимый экземпляр.
- emitter.on('addImage', this.handler, this) — подписывает метод handler текущей сцены на событие 'addImage'. Третий аргумент (this) задает контекст (this внутри handler), что критически важно для корректного доступа к методам сцены, таким как this.add.image.
Генерация событий с данными
После настройки подписки можно генерировать события в любом месте кода, где доступна переменная emitter. Метод emit() принимает имя события и любое количество аргументов, которые будут переданы в обработчик.
// Emit it a few times with varying arguments
emitter.emit('addImage', 200, 300);
emitter.emit('addImage', 400, 300);
emitter.emit('addImage', 600, 300);
Каждый вызов emit('addImage', x, y) приводит к вызову функции handler(x, y), которая получает переданные координаты `xиy` в качестве параметров. Это демонстрирует, как можно передавать данные от источника события к подписчику.
Обработчик события и его роль
Функция-обработчик, подписанная на событие, содержит логику, которая должна выполниться при наступлении этого события. В нашем случае обработчик создает изображение по заданным координатам.
handler (x, y)
{
this.add.image(x, y, 'plush');
}
Благодаря тому, что контекст был передан как this при подписке, внутри handler мы можем без проблем использовать this.add.image. Без указания контекста this внутри handler был бы равен самому объекту emitter, что привело бы к ошибке.
Что попробовать дальше
Создание собственных экземпляров EventEmitter — это отличный паттерн для декомпозиции игровой логики в Phaser. Он позволяет создавать слабосвязанные и переиспользуемые системы. Для экспериментов попробуйте: создать эмиттер для системы звуков, которая генерирует события 'playSound'; использовать отдельный эмиттер для управления состоянием UI-панели; или организовать обмен сообщениями между несколькими независимыми игровыми объектами (не GameObject) через общий или несколько отдельных эмиттеров.
