О чем этот пример
В Phaser система анимаций построена вокруг центрального менеджера — `this.anims`. Часто возникает задача автоматически отреагировать, когда в игре создается новая анимация. Например, вы хотите немедленно создать спрайт и проиграть на нем только что добавленную анимацию. В этой статье мы разберем, как использовать событие `ADD_ANIMATION` для создания реактивной логики, которая делает ваш код чище и более модульным, избавляя от необходимости вручную связывать создание анимации и её использование.
Версия 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.atlas('gems', 'assets/tests/columns/gems.png', 'assets/tests/columns/gems.json');
// Local variable
this.y = 160;
}
create ()
{
this.add.text(400, 32, 'Click to create animations', { color: '#00ff00' })
.setOrigin(0.5, 0);
// Each time a new animation is added to the Animation Manager we'll call this function
this.anims.on(Phaser.Animations.Events.ADD_ANIMATION, this.addAnimation, this);
this.i = 0;
// Click to add an animation
this.input.on('pointerup', function () {
switch (this.i)
{
case 0:
this.anims.create({ key: 'diamond', frames: this.anims.generateFrameNames('gems', { prefix: 'diamond_', end: 15, zeroPad: 4 }), repeat: -1 });
break;
case 1:
this.anims.create({ key: 'prism', frames: this.anims.generateFrameNames('gems', { prefix: 'prism_', end: 6, zeroPad: 4 }), repeat: -1 });
break;
case 2:
this.anims.create({ key: 'ruby', frames: this.anims.generateFrameNames('gems', { prefix: 'ruby_', end: 6, zeroPad: 4 }), repeat: -1 });
break;
case 3:
this.anims.create({ key: 'square', frames: this.anims.generateFrameNames('gems', { prefix: 'square_', end: 14, zeroPad: 4 }), repeat: -1 });
break;
}
this.i++;
}, this);
}
addAnimation (key)
{
this.add.sprite(400, this.y, 'gems')
.play(key);
this.y += 100;
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Подписка на событие добавления анимации
Ядро механики — это событие Phaser.Animations.Events.ADD_ANIMATION. Менеджер анимаций this.anims является экземпляром EventEmitter, что позволяет подписываться на различные события его жизненного цикла.
Мы регистрируем обработчик в методе create, что гарантирует его готовность до первого клика пользователя.
this.anims.on(Phaser.Animations.Events.ADD_ANIMATION, this.addAnimation, this);
Ключевые моменты:
- `Phaser.Animations.Events.ADD_ANIMATION` — это константа, представляющая имя события.
- `this.addAnimation` — метод, который будет вызван при срабатывании события.
- Третий аргумент `this` задаёт контекст, в котором будет выполнен метод-обработчик. Без этого контекст был бы потерян, и мы не смогли бы получить доступ к `this.add.sprite` или `this.y` внутри `addAnimation`.
Создание анимаций по требованию
В примере анимации создаются не сразу, а в ответ на действие пользователя — клик мышью. Это отлично демонстрирует асинхронную природу события: мы не знаем заранее, когда и какая анимация будет добавлена.
Вот как устроен обработчик клика:
this.input.on('pointerup', function () {
switch (this.i)
{
case 0:
this.anims.create({ key: 'diamond', frames: this.anims.generateFrameNames('gems', { prefix: 'diamond_', end: 15, zeroPad: 4 }), repeat: -1 });
break;
case 1:
this.anims.create({ key: 'prism', frames: this.anims.generateFrameNames('gems', { prefix: 'prism_', end: 6, zeroPad: 4 }), repeat: -1 });
break;
case 2:
this.anims.create({ key: 'ruby', frames: this.anims.generateFrameNames('gems', { prefix: 'ruby_', end: 6, zeroPad: 4 }), repeat: -1 });
break;
case 3:
this.anims.create({ key: 'square', frames: this.anims.generateFrameNames('gems', { prefix: 'square_', end: 14, zeroPad: 4 }), repeat: -1 });
break;
}
this.i++;
}, this);
Каждый вызов this.anims.create() инициирует цепочку событий. Сразу после успешного создания анимации в менеджере срабатывает событие ADD_ANIMATION. Обратите внимание на параметры generateFrameNames: они используются для автоматической генерации кадров анимации из атласа, используя префикс имени и конечный индекс.
Обработчик события и его логика
Метод addAnimation — это наш реактивный обработчик. Он автоматически получает ключ (key) созданной анимации в качестве аргумента.
Вот его реализация:
addAnimation (key)
{
this.add.sprite(400, this.y, 'gems')
.play(key);
this.y += 100;
}
Что происходит:
1. Создается новый спрайт в позиции `(400, this.y)`. Текстура спрайта — тот же атлас `'gems'`, из которого сделаны кадры анимации.
2. На свежесозданном спрайте немедленно запускается анимация с только что переданным ключом `key` с помощью метода `.play()`.
3. Переменная `this.y` увеличивается на 100 пикселей, чтобы следующий спрайт появился ниже, создавая вертикальную колонку.
Таким образом, связь между созданием анимации и её визуальным представлением на сцене становится декларативной и управляемой событиями.
Практические сценарии применения
Шаблон «событие при добавлении анимации» выходит за рамки простого демо. Вот где он действительно полезен:
* **Динамическая загрузка контента:** Если вы загружаете данные об анимациях из внешнего JSON-файла или с сервера и создаете их партиями, событие ADD_ANIMATION может триггерить предзагрузку связанных ресурсов или обновление UI (например, добавление иконки способности в меню).
* **Централизованная отладка и логирование:** Вы можете добавить глобальный обработчик, который логирует в консоль каждую созданную анимацию, её ключ и параметры, что бесценно при отладке больших проектов.
* **Автоматическая привязка к объектам пула:** В играх с объектным пулом (например, пулевых стрелялках) вы можете настроить систему так, чтобы при создании анимации взрыва 'explosion' все спрайты из пула «взрывов» автоматически получали на неё ссылку и могли её проиграть по требованию.
// Пример логирования всех создаваемых анимаций
this.anims.on(Phaser.Animations.Events.ADD_ANIMATION, function(key, animation) {
console.log('Анимация создана:', key, animation.frames.length, 'кадров');
}, this);
Обратите внимание, что полная сигнатура обработчика может также включать вторым аргументом сам экземпляр класса Animation.
Что попробовать дальше
Использование события ADD_ANIMATION — это мощный паттерн для создания слабосвязанных и реактивных систем в Phaser. Он позволяет отделить код, который определяет анимации, от кода, который их использует. Для экспериментов попробуйте
- создать систему, где анимации добавляются не по клику, а по таймеру
- модифицировать обработчик так, чтобы спрайты появлялись в случайных позициях на экране
- подписаться на другие события менеджера анимаций, например,
REMOVE_ANIMATION, чтобы очищать ресурсы
