AnimationState и управление через компонент

В Phaser система анимации построена на разделении данных и поведения. Глобальный менеджер анимаций хранит определения, а каждый отображаемый объект (Sprite, Image с текстурой атласа и др.) получает собственный компонент AnimationState, отвечающий за проигрывание, переключение и управление текущим состоянием анимации.

AnimationState — это не отдельный класс, который создаётся вручную, а встроенный компонент, автоматически добавляемый к объектам типа Phaser.GameObjects.Sprite. Он инкапсулирует:

  • текущую активную анимацию;
  • состояние воспроизведения (play, pause, stop);
  • направление проигрывания;
  • частоту кадров;
  • повторения;
  • колбэки событий;
  • очередь отложенного запуска.

Архитектура AnimationState

Связь с Animation Manager

Внутри сцены (Scene) существует менеджер анимаций:

this.anims

Он хранит глобальные определения анимаций. Компонент AnimationState конкретного спрайта обращается к этому менеджеру при запуске:

sprite.play('run');

Происходит следующий процесс:

  1. По ключу 'run' ищется глобальное определение.
  2. Создаётся экземпляр проигрывания.
  3. AnimationState начинает управлять кадрами.

Таким образом:

  • Animation Manager — хранит описание.
  • AnimationState — управляет проигрыванием.

Создание анимации и запуск через AnimationState

Определение анимации

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

Запуск

player.play('run');

Метод play() принадлежит компоненту AnimationState.


Внутреннее состояние компонента

Компонент содержит ряд ключевых свойств:

currentAnim

Ссылка на текущую анимацию. Если равна null, проигрывание отсутствует.

currentFrame

Объект текущего кадра.

isPlaying

Булево значение, отражающее активное проигрывание.

isPaused

Пауза без сброса позиции.

progress

Нормализованное значение (0–1), отражающее прогресс проигрывания.


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

Запуск

sprite.play('attack');

С перезапуском:

sprite.play('attack', true);

Второй аргумент ignoreIfPlaying предотвращает повторный запуск.


Пауза

sprite.anims.pause();

Возобновление:

sprite.anims.resume();

Полная остановка

sprite.anims.stop();

Сброс к первому кадру:

sprite.anims.stop('run');

Управление скоростью

Изменение frameRate

sprite.anims.setFrameRate(20);

Изменение скорости через timeScale

sprite.anims.timeScale = 2;

timeScale умножает базовую скорость:

  • 1 — нормальная скорость
  • 2 — в 2 раза быстрее
  • 0.5 — в 2 раза медленнее

Повторения и завершение

Настройка повторений

При создании:

repeat: 3

Значения:

  • 0 — один раз
  • >0 — указанное количество повторений
  • -1 — бесконечно

Проверка завершения

sprite.anims.on('animationcomplete', callback);

Также возможно отследить конкретную анимацию:

sprite.on('animationcomplete-attack', callback);

Очередь анимаций

AnimationState поддерживает очередь.

sprite.play('attack');
sprite.chain('idle');

После завершения attack автоматически начнётся idle.

Также возможно:

sprite.anims.queue('idle');

Переключение состояний персонажа

AnimationState удобно использовать как часть логики конечного автомата (FSM).

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

if (body.velocity.x !== 0) {
    player.play('run', true);
} else {
    player.play('idle', true);
}

Аргумент true предотвращает перезапуск при уже активной анимации.


Работа с направлением проигрывания

Реверс

sprite.anims.reverse();

Принудительное направление

sprite.anims.playReverse('run');

Переход к конкретному кадру

sprite.anims.setCurrentFrame(
    sprite.anims.currentAnim.frames[3]
);

Также доступно:

sprite.anims.setProgress(0.5);

События AnimationState

Компонент генерирует ряд событий:

  • animationstart
  • animationupdate
  • animationrepeat
  • animationcomplete
  • animationstop

Пример:

sprite.on('animationstart', (anim) => {
    console.log('Начало:', anim.key);
});

Локальные анимации

Помимо глобальных анимаций, AnimationState поддерживает локальные.

sprite.anims.create({
    key: 'localAnim',
    frames: [...]
});

Такая анимация доступна только этому объекту.


Взаимодействие с физикой

При использовании физики (Arcade Physics) часто требуется синхронизация движения и анимации.

if (player.body.velocity.y < 0) {
    player.play('jump', true);
}

В Phaser обновление анимации происходит автоматически в цикле Scene.update().


Остановка на последнем кадре

При создании:

repeat: 0,
hideOnComplete: false

Либо вручную:

sprite.on('animationcomplete', () => {
    sprite.anims.stop();
});

Гибкая настройка через конфигурацию play()

Метод play() принимает объект:

sprite.anims.play({
    key: 'attack',
    frameRate: 30,
    repeat: 2,
    delay: 100
});

Это позволяет динамически менять поведение без пересоздания анимации.


Синхронизация нескольких объектов

enemy1.play('walk');
enemy2.play('walk');

Для точной синхронизации:

enemy2.anims.setProgress(enemy1.anims.progress);

Работа с задержкой старта

sprite.anims.playAfterDelay('attack', 500);

Запуск через 500 мс.


Продвинутое управление через update()

В редких случаях требуется ручной контроль:

sprite.anims.update(time, delta);

Обычно не используется, так как система обновляется автоматически.


Оптимизация

  • Не вызывать play() каждый кадр без ignoreIfPlaying.
  • Использовать chain() вместо логики через события при последовательностях.
  • Избегать создания анимаций в update().

Роль AnimationState в компонентной модели

AnimationState реализует изолированное управление визуальным состоянием объекта:

  • не зависит от логики ввода;
  • не зависит от физики;
  • не управляет позиционированием;
  • отвечает исключительно за временную смену кадров.

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


Практический паттерн: централизованный метод смены состояния

В крупных проектах удобно выделять метод:

setState(state) {
    if (this.state === state) return;

    this.state = state;
    this.play(state, true);
}

Это снижает количество условных операторов в update() и делает логику читаемой.


Глубокая интеграция с игровыми состояниями

AnimationState становится визуальным отражением логического состояния:

  • idle
  • run
  • jump
  • attack
  • hurt
  • death

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

Компонент анимации в Phaser — это полноценная подсистема управления временными состояниями спрайта, предоставляющая гибкий, событийный и высокопроизводительный механизм визуализации динамики игровых объектов.