В библиотеке 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');
});
Такой подход обеспечивает гибкость и возможность динамической логики переходов.
В более современных версиях Phaser реализован встроенный механизм
цепочек через метод chain() у
AnimationState.
hero.play('attack_start').chain('attack_end');
После завершения attack_start автоматически запускается
attack_end.
Можно передавать массив:
hero.play('attack_start').chain(['attack_loop', 'attack_end']);
Особенности метода:
Преимущество — лаконичность и отсутствие необходимости вручную обрабатывать события.
Иногда требуется временный цикл перед переходом к финальной фазе. Например:
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();
Это особенно важно при резком изменении состояния персонажа (например, получение урона прерывает атаку).
Помимо завершения всей анимации существует событие кадра:
hero.on('animationupdate', (animation, frame) => {
if (animation.key === 'attack_start' && frame.index === 3) {
spawnHitbox();
}
});
Такой механизм позволяет:
При использовании цепочек события продолжают работать для каждой анимации отдельно.
В сложных проектах цепочки анимаций интегрируются в архитектуру конечных автоматов (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');
}
});
Такой подход позволяет реализовать:
Объект hero.anims предоставляет низкоуровневый
контроль:
isPlayingcurrentAnimgetProgress()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');
Такая запись формирует последовательную сцену без дополнительных таймеров.
Анимации могут формироваться из:
Цепочки работают одинаково независимо от источника кадров. Главное —
корректная регистрация анимаций через
this.anims.create().
chain() при линейной
последовательности.Цепочки анимаций являются важным инструментом построения выразительного поведения игровых объектов, обеспечивая плавность переходов и структурированную организацию визуальных сценариев внутри игрового цикла.