События анимации: start, repeat, complete, animationupdate

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

События позволяют реагировать на различные этапы жизненного цикла анимации:

  • запуск воспроизведения (start)
  • повтор цикла (repeat)
  • завершение (complete)
  • обновление кадра (animationupdate)

Обработчики событий подключаются через систему событий EventEmitter, встроенную в игровые объекты (Sprite, Image, TileSprite и др.).


Архитектура анимаций и источник событий

Анимации в Phaser определяются через менеджер анимаций сцены:

this.anims.create({
    key: 'run',
    frames: this.anims.generateFrameNumbers('player', { start: 0, end: 5 }),
    frameRate: 10,
    repeat: -1
});

При воспроизведении:

player.play('run');

объект player (экземпляр Phaser.GameObjects.Sprite) начинает генерировать события анимации.

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


Событие start

Момент возникновения

Событие animationstart (сокращённо start в контексте анимации) возникает в момент начала воспроизведения анимации — когда она фактически стартует, а не просто назначается.

Подключение обработчика

player.on('animationstart', function (animation, frame) {
    console.log('Анимация запущена:', animation.key);
});

Передаваемые параметры

  • animation — объект анимации
  • frame — первый активный кадр
  • gameObject — объект, воспроизводящий анимацию

Практическое применение

  • запуск звукового эффекта при старте
  • блокировка управления персонажем
  • изменение состояния конечного автомата

Пример с изменением состояния:

player.on('animationstart', function (animation) {
    if (animation.key === 'attack') {
        player.isAttacking = true;
    }
});

Событие repeat

Назначение

Событие animationrepeat генерируется каждый раз, когда анимация с параметром repeat > 0 или repeat: -1 начинает новый цикл.

Пример определения анимации с повтором

this.anims.create({
    key: 'blink',
    frames: this.anims.generateFrameNumbers('enemy', { start: 0, end: 2 }),
    frameRate: 5,
    repeat: 3
});

Подписка на событие

enemy.on('animationrepeat', function (animation, frame) {
    console.log('Повтор анимации:', animation.key);
});

Поведение при repeat: -1

При бесконечном повторе событие repeat будет вызываться при каждом переходе от последнего кадра к первому.

Практическое использование

  • постепенное изменение характеристик объекта
  • накопление счётчика циклов
  • запуск побочной логики

Пример подсчёта повторов:

let repeatCount = 0;

enemy.on('animationrepeat', function () {
    repeatCount++;
});

Событие complete

Момент вызова

Событие animationcomplete вызывается при полном завершении анимации, если:

  • repeat имеет конечное значение
  • анимация не была прервана методом stop()
  • не была запущена другая анимация

Подписка

player.on('animationcomplete', function (animation, frame) {
    console.log('Анимация завершена:', animation.key);
});

Фильтрация по ключу

Phaser автоматически генерирует события вида:

animationcomplete-ключ

Пример:

player.on('animationcomplete-attack', function () {
    player.isAttacking = false;
});

Такой подход исключает необходимость вручную проверять animation.key.

Практические сценарии

  • переключение в состояние ожидания
  • удаление объекта после проигрывания анимации смерти
  • переход к следующей фазе поведения

Пример удаления врага:

enemy.on('animationcomplete-death', function () {
    enemy.destroy();
});

Событие animationupdate

Особенности

animationupdate вызывается при каждом изменении кадра анимации. Это наиболее часто генерируемое событие и используется для синхронизации логики с конкретными кадрами.

Подключение

player.on('animationupdate', function (animation, frame) {
    console.log('Текущий кадр:', frame.index);
});

Передаваемые параметры

  • animation
  • frame
  • gameObject
  • frameKey (в зависимости от типа кадров)

Контроль конкретного кадра

player.on('animationupdate', function (animation, frame) {
    if (animation.key === 'attack' && frame.index === 3) {
        spawnHitbox();
    }
});

Так реализуется точная синхронизация удара с визуальным моментом.

Производительность

Поскольку событие вызывается на каждом кадре анимации, его использование требует аккуратности:

  • избегать тяжёлых вычислений
  • не создавать новые объекты внутри обработчика
  • не выполнять сложные запросы к сцене

Различие между stop и complete

Если анимация останавливается методом:

player.anims.stop();

событие complete не будет вызвано. В этом случае можно использовать событие animationstop:

player.on('animationstop', function (animation) {
    console.log('Анимация остановлена вручную');
});

Управление подписками

Однократная подписка

player.once('animationcomplete', function () {
    console.log('Сработает один раз');
});

Отписка

function onComplete(animation) {
    console.log(animation.key);
}

player.on('animationcomplete', onComplete);
player.off('animationcomplete', onComplete);

Правильное управление подписками предотвращает утечки памяти и дублирование логики.


Взаимодействие с конечным автоматом состояний

События анимации часто используются совместно с FSM (Finite State Machine). Например:

class Player {
    constructor(sprite) {
        this.sprite = sprite;
        this.state = 'idle';

        sprite.on('animationcomplete-attack', () => {
            this.setState('idle');
        });
    }

    setState(newState) {
        this.state = newState;
        this.sprite.play(newState);
    }
}

В этом подходе завершение анимации напрямую инициирует переход состояния.


Приоритет событий при смене анимации

Если во время выполнения анимации запускается другая:

player.play('run');

текущая анимация:

  • не вызовет complete
  • будет остановлена
  • сгенерирует animationstop

Новая анимация сгенерирует start.

Порядок событий:

  1. animationstop
  2. animationstart

Понимание этого порядка критично для сложной логики поведения.


Использование событий в группах объектов

Каждый спрайт генерирует собственные события. Для массовой обработки:

this.enemies.children.iterate(enemy => {
    enemy.on('animationcomplete-death', function () {
        enemy.destroy();
    });
});

События не распространяются автоматически по группе; обработчики назначаются индивидуально.


Синхронизация анимации и физики

При использовании модуля физики (Arcade Physics):

player.on('animationupdate', function (animation, frame) {
    if (animation.key === 'jump' && frame.index === 2) {
        player.body.setVelocityY(-300);
    }
});

Это позволяет запускать импульс строго в нужный момент визуальной фазы прыжка.


События и временная шкала

Анимация работает независимо от update() сцены. События испускаются на основе внутреннего таймера анимации (frameRate, timeScale). Это обеспечивает:

  • детерминированность
  • независимость от логики обновления сцены
  • корректную работу при изменении timeScale

Расширенные возможности

Глобальное прослушивание

Менеджер анимаций сцены также испускает события, но в большинстве случаев предпочтительна работа через конкретный объект.

Динамическая смена скорости

Изменение скорости анимации во время выполнения:

player.anims.timeScale = 2;

События update, repeat, complete будут происходить быстрее, но их логическая последовательность сохранится.


Типичные ошибки

1. Подписка до создания объекта

Обработчик должен назначаться после создания спрайта.

2. Множественная подписка в update()

Недопустимо:

update() {
    player.on('animationcomplete', handler);
}

Это приводит к лавинообразному росту обработчиков.

3. Игнорирование прерывания анимации

Логика, завязанная только на complete, может не выполниться при смене анимации.


Стратегия построения реактивной логики

Эффективная модель работы с анимациями включает:

  • запуск логики в start
  • реакцию на ключевые кадры через animationupdate
  • обработку циклов через repeat
  • завершение состояния через complete
  • контроль прерываний через stop

Такая архитектура делает систему поведения предсказуемой и масштабируемой при разработке сложных игровых механик.