Анимация
Покадровые анимации из спрайт-листов и атласов — определение, воспроизведение, объединение в цепочки и реакция на события анимации.
Система анимации Phaser 4 воспроизводит последовательности кадров текстуры на объекте Sprite. Анимации определяются один раз на уровне сцены (или глобально), а затем воспроизводятся на любом количестве спрайтов — определение и воспроизведение разделены.
Где хранятся анимации
Два места:
this.anims— менеджер анимаций уровня сцены. Анимации хранятся здесь по умолчанию.this.game.anims(редко) — глобальный менеджер, который можно использовать для совместного использования определений между сценами.
Почти всегда используйте менеджер уровня сцены. Совместное использование между сценами склонно к утечкам состояния.
Определение анимации
Нужен источник — спрайт-лист (равномерные кадры) или атлас (именованные кадры, нерегулярные):
preload() {
// Равномерная сетка.
this.load.spritesheet('dude', 'assets/dude.png', { frameWidth: 32, frameHeight: 48 });
// Атлас с именованными кадрами.
this.load.atlas('hero', 'assets/hero.png', 'assets/hero.json');
}
create() {
this.anims.create({
key: 'walk',
frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 3 }),
frameRate: 10,
repeat: -1, // -1 означает бесконечный цикл
});
this.anims.create({
key: 'attack',
frames: this.anims.generateFrameNames('hero', { prefix: 'attack-', start: 1, end: 6, zeroPad: 2 }),
frameRate: 18,
repeat: 0, // воспроизвести один раз
});
}
frameRate — это кадры в секунду, а не миллисекунды на кадр. Альтернатива — duration, общая длительность анимации в мс; она переопределяет frameRate, если заданы оба значения.
Воспроизведение на спрайте
const player = this.add.sprite(100, 100, 'dude');
player.play('walk'); // запустить (или перезапустить) немедленно
player.play({ key: 'walk', repeat: 3 }); // переопределить конфигурацию для этого воспроизведения
player.playAfterDelay('walk', 250); // запустить через 250 мс
player.chain('attack'); // поставить в очередь анимацию для воспроизведения по окончании текущей
player.stop(); // остановиться на текущем кадре
player.stopAfterRepeat(); // завершить текущий цикл, затем остановиться
Живой пример
Шагающий персонаж — определён один раз, воспроизводится на одном спрайте.
Реакция на воспроизведение
Анимации генерируют события на воспроизводящем их спрайте:
player.on('animationcomplete', (anim) => {
if (anim.key === 'attack') player.play('idle');
});
player.on('animationrepeat', (anim) => {
if (anim.key === 'walk') this.playFootstep();
});
player.on('animationupdate', (anim, frame) => {
// Срабатывает один раз при каждой смене кадра.
});
Для сценария «проиграть X один раз, затем вернуться к Y» API chain чище, чем ручная обработка animationcomplete:
player.play('attack').chain('idle');
Распространённые ошибки
Определение внутри update. anims.create идемпотентен (он выдаёт предупреждение при дублировании ключей), но не следует вызывать его каждый кадр. Определяйте анимации в create.
Состояние на спрайт против состояния на ключ. Определение анимации является общим, но у каждого спрайта своя позиция воспроизведения. Воспроизведение одной и той же анимации на двух спрайтах выполняется независимо.
Порядок загрузки. generateFrameNumbers и generateFrameNames требуют, чтобы исходная текстура уже существовала в кэше, поэтому вызывайте их из create() (после завершения preload), а не раньше.
Связанные материалы
- Загрузчик и ресурсы — как спрайт-листы и атласы попадают в кэш.
- Игровые объекты —
Sprite— единственный встроенный тип с прикреплённым менеджером анимаций.