О чем этот пример
Внутренняя система событий 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 для подписки на событие, которое должно сработать только один раз (например, завершение обучения).
