Цепочки анимаций

В библиотеке Phaser анимация представляет собой последовательность кадров, воспроизводимых с заданной частотой. В реальных игровых проектах редко используется одна изолированная анимация. Игровой объект может переходить из состояния в состояние: ожидание, движение, прыжок, атака, получение урона. Для управления такими переходами применяется механизм цепочек анимаций.

Цепочка анимаций — это последовательный запуск нескольких анимаций у одного и того же объекта без внешнего вмешательства. После завершения одной анимации автоматически стартует следующая. Такой подход позволяет:

  • формировать сложные сценарии поведения;
  • управлять фазами атак или способностей;
  • организовывать кинематографические сцены;
  • синхронизировать визуальные эффекты.

Базовая настройка анимаций

Перед построением цепочки необходимо создать сами анимации через менеджер this.anims.

this.anims.create({
    key: 'attack_start',
    frames: this.anims.generateFrameNumbers('hero', { start: 0, end: 4 }),
    frameRate: 12,
    repeat: 0
});

this.anims.create({
    key: 'attack_loop',
    frames: this.anims.generateFrameNumbers('hero', { start: 5, end: 8 }),
    frameRate: 12,
    repeat: -1
});

this.anims.create({
    key: 'attack_end',
    frames: this.anims.generateFrameNumbers('hero', { start: 9, end: 12 }),
    frameRate: 12,
    repeat: 0
});

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


Запуск последовательности через события

Самый универсальный способ построения цепочки — использование события animationcomplete.

hero.play('attack_start');

hero.on('animationcomplete', (animation) => {
    if (animation.key === 'attack_start') {
        hero.play('attack_loop');
    }

    if (animation.key === 'attack_end') {
        hero.play('idle');
    }
});

Ключевые особенности:

  • событие срабатывает только у анимаций с repeat: 0;
  • для бесконечных анимаций событие завершения не возникает;
  • можно подписываться на конкретную анимацию:
hero.on('animationcomplete-attack_start', () => {
    hero.play('attack_loop');
});

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


Метод chain()

В более современных версиях Phaser реализован встроенный механизм цепочек через метод chain() у AnimationState.

hero.play('attack_start').chain('attack_end');

После завершения attack_start автоматически запускается attack_end.

Можно передавать массив:

hero.play('attack_start').chain(['attack_loop', 'attack_end']);

Особенности метода:

  • работает только для анимаций без бесконечного повтора;
  • порядок строго последовательный;
  • цепочка очищается после выполнения.

Преимущество — лаконичность и отсутствие необходимости вручную обрабатывать события.


Комбинирование repeat и цепочек

Иногда требуется временный цикл перед переходом к финальной фазе. Например:

  • подготовка;
  • три цикла вращения;
  • завершение.
this.anims.create({
    key: 'spin',
    frames: this.anims.generateFrameNumbers('hero', { start: 0, end: 5 }),
    frameRate: 10,
    repeat: 2
});

hero.play('spin').chain('attack_end');

Анимация spin выполнится три раза (2 повтора + первый цикл), затем автоматически перейдёт к attack_end.


Управление цепочкой во время выполнения

Цепочку можно изменить динамически:

hero.anims.stop();
hero.play('jump_start').chain('jump_fall');

Также возможна очистка:

hero.anims.removeChain();

Это особенно важно при резком изменении состояния персонажа (например, получение урона прерывает атаку).


Использование Animation Events внутри цепочки

Помимо завершения всей анимации существует событие кадра:

hero.on('animationupdate', (animation, frame) => {
    if (animation.key === 'attack_start' && frame.index === 3) {
        spawnHitbox();
    }
});

Такой механизм позволяет:

  • синхронизировать урон;
  • создавать эффекты частиц;
  • запускать звук в точный момент.

При использовании цепочек события продолжают работать для каждой анимации отдельно.


Цепочки и State Machine

В сложных проектах цепочки анимаций интегрируются в архитектуру конечных автоматов (FSM). Каждое состояние управляет своей последовательностью.

Пример логики состояния:

class AttackState {
    enter(hero) {
        hero.play('attack_start').chain('attack_end');
    }

    update(hero) {
        if (hero.body.onFloor() === false) {
            hero.setState('fall');
        }
    }
}

Цепочка отвечает только за визуальную последовательность, а FSM — за глобальную логику переходов.


Параллельные анимации и ограничения

В Phaser один Sprite может воспроизводить только одну анимацию одновременно. Поэтому цепочки относятся только к последовательному проигрыванию.

Если требуется:

  • анимация персонажа,
  • одновременный эффект оружия,

используются отдельные объекты:

hero.play('attack_start');
weapon.play('slash_effect');

Каждый объект может иметь собственную цепочку.


Тайминги и задержки

Иногда между анимациями необходима пауза. Для этого используется таймер сцены:

hero.play('attack_start');

hero.on('animationcomplete-attack_start', () => {
    this.time.delayedCall(200, () => {
        hero.play('attack_end');
    });
});

Так достигается более кинематографичный эффект.


Цепочки с динамическим выбором следующей анимации

В зависимости от условий следующая анимация может отличаться:

hero.on('animationcomplete-attack_start', () => {
    if (hero.energy > 50) {
        hero.play('power_attack');
    } else {
        hero.play('attack_end');
    }
});

Такой подход позволяет реализовать:

  • комбо-системы;
  • альтернативные ветви атак;
  • контекстные переходы.

Работа с AnimationState напрямую

Объект hero.anims предоставляет низкоуровневый контроль:

  • isPlaying
  • currentAnim
  • getProgress()
  • pause()
  • resume()

Пример проверки прогресса:

if (hero.anims.getProgress() > 0.8) {
    hero.setVelocityX(0);
}

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


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

Цепочки сами по себе не создают дополнительной нагрузки, однако следует учитывать:

  • чрезмерное количество слушателей событий;
  • постоянное создание временных анимаций;
  • отсутствие удаления подписок.

Оптимальная практика — регистрировать события один раз при создании объекта и переиспользовать их.


Распространённые ошибки

1. Попытка зацепить бесконечную анимацию

Анимация с repeat: -1 не завершится, следовательно, цепочка не перейдёт дальше.

2. Множественные подписки

При создании обработчиков внутри update() возникает накопление слушателей.

3. Прерывание без очистки

Если анимация прерывается через stop(), цепочка может не завершиться корректно.


Расширенные сценарии

Комбо-система

Каждое нажатие кнопки во время окна анимации запускает следующую фазу:

hero.on('animationcomplete-attack_1', () => {
    if (this.input.activePointer.isDown) {
        hero.play('attack_2');
    } else {
        hero.play('idle');
    }
});

Кинематографическая сцена

hero.play('look_around')
    .chain('draw_sword')
    .chain('battle_stance');

Такая запись формирует последовательную сцену без дополнительных таймеров.


Взаимодействие со спрайт-листами и атласами

Анимации могут формироваться из:

  • sprite sheet;
  • texture atlas;
  • отдельных изображений.

Цепочки работают одинаково независимо от источника кадров. Главное — корректная регистрация анимаций через this.anims.create().


Архитектурные рекомендации

  1. Анимации должны отражать состояния, а не логику.
  2. Цепочки используются для визуальных переходов.
  3. Управление глобальными переходами — через систему состояний.
  4. Минимизируется количество вложенных событий.
  5. Предпочтение отдается chain() при линейной последовательности.

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