~1 мин чтения

Анимация

Покадровые анимации из спрайт-листов и атласов — определение, воспроизведение, объединение в цепочки и реакция на события анимации.

Система анимации 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();            // завершить текущий цикл, затем остановиться

Живой пример

Шагающий персонаж — определён один раз, воспроизводится на одном спрайте.

Walking dude Phaser 4 · sandboxed

Реакция на воспроизведение

Анимации генерируют события на воспроизводящем их спрайте:

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), а не раньше.

Связанные материалы