О чем этот пример
Плавные переходы между состояниями персонажа — ключ к созданию живой игровой сцены. Часто требуется не просто запустить анимацию, а сделать это с задержкой, например, чтобы синхронизировать её с другими событиями или создать эффект ожидания. В этой статье мы разберем, как использовать метод `playAfterDelay()` для отложенного проигрывания анимаций в Phaser 3. На практическом примере с анимированным зомби вы научитесь управлять временем запуска анимаций, что особенно полезно для создания сцен смерти, реакций на удар или последовательных действий.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('zombie', 'assets/animations/zombie.png', 'assets/animations/zombie.json');
this.load.image('bg', 'assets/textures/soil.png');
}
create ()
{
this.bg = this.add.tileSprite(400, 300, 800, 600, 'bg').setAlpha(0.8);
const text = this.add.text(400, 32, "Click to run playAfterDelay('death', 2000)", { color: '#00ff00' }).setOrigin(0.5, 0);
// Our global animations, as defined in the texture atlas
this.anims.create({ key: 'walk', frames: this.anims.generateFrameNames('zombie', { prefix: 'walk_', end: 8, zeroPad: 3 }), repeat: -1, frameRate: 8 });
this.anims.create({ key: 'death', frames: this.anims.generateFrameNames('zombie', { prefix: 'Death_', end: 5, zeroPad: 3 }), frameRate: 12 });
this.rob = this.add.sprite(400, 560, 'zombie').setOrigin(0.5, 1).play('walk');
this.input.once('pointerdown', function () {
text.setText('Playing death animation in 2000 ms');
this.rob.anims.playAfterDelay('death', 2000);
}, this);
}
update ()
{
if (this.rob.anims.getName() === 'walk')
{
this.bg.tilePositionY++;
}
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example
};
const game = new Phaser.Game(config);
Инициализация сцены и загрузка ассетов
В методе preload() происходит загрузка необходимых ресурсов. Используется this.load.setBaseURL() для указания базового URL, откуда будут загружаться файлы. Это удобно для примеров, но в реальном проекте лучше использовать относительные пути или свой CDN.
Загружаются два ключевых ассета: атлас анимаций zombie (состоящий из PNG-изображения и JSON-файла с данными о кадрах) и фоновая текстура bg.
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('zombie', 'assets/animations/zombie.png', 'assets/animations/zombie.json');
this.load.image('bg', 'assets/textures/soil.png');
Создание анимаций и игровых объектов
В методе create() происходит настройка игрового мира. Сначала создается фоновый спрайт this.bg с помощью this.add.tileSprite. Установка alpha на 0.8 делает его полупрозрачным.
Затем создается текстовый объект, который будет выводить инструкции для игрока.
Ключевой шаг — создание глобальных анимаций через this.anims.create(). Для каждой анимации (walk и death) с помощью this.anims.generateFrameNames() генерируются имена кадров из атласа. Анимация walk зациклена (repeat: -1), а death проигрывается один раз.
Создается спрайт зомби this.rob и сразу запускается на нём анимация ходьбы с помощью .play('walk').
this.anims.create({ key: 'walk', frames: this.anims.generateFrameNames('zombie', { prefix: 'walk_', end: 8, zeroPad: 3 }), repeat: -1, frameRate: 8 });
this.anims.create({ key: 'death', frames: this.anims.generateFrameNames('zombie', { prefix: 'Death_', end: 5, zeroPad: 3 }), frameRate: 12 });
this.rob = this.add.sprite(400, 560, 'zombie').setOrigin(0.5, 1).play('walk');
Запуск анимации с задержкой по событию
Здесь реализуется основная логика примера. На сцену вешается одноразовый обработчик события клика (this.input.once('pointerdown', ...)). При клике текст на экране обновляется.
Самое важное — вызов метода playAfterDelay() у компонента анимаций спрайта this.rob. Первым аргументом передаётся ключ анимации ('death'), которую нужно проиграть, а вторым — задержка в миллисекундах (2000 мс = 2 секунды). Этот метод планирует запуск указанной анимации через заданный промежуток времени, прерывая текущую.
this.rob.anims.playAfterDelay('death', 2000);
Обновление состояния сцены
Метод update() вызывается на каждом кадре. В нём проверяется, какая анимация сейчас проигрывается на спрайте this.rob. Условие this.rob.anims.getName() === 'walk' возвращает строковый ключ текущей анимации.
Если зомби идёт (проигрывается анимация walk), то фоновая текстура this.bg медленно прокручивается вниз (увеличивается её tilePositionY). Это создаёт простой эффект движения. Как только будет запущена анимация death, это условие перестанет выполняться, и прокрутка фона остановится.
if (this.rob.anims.getName() === 'walk')
{
this.bg.tilePositionY++;
}
Что попробовать дальше
Метод playAfterDelay() — это простой и эффективный способ управлять временем в ваших анимациях. Он идеально подходит для создания пауз между действиями, отложенных реакций или построения простых анимационных последовательностей.
**Идеи для экспериментов:**
1. Попробуйте вызвать playAfterDelay() несколько раз подряд с разными анимациями и задержками, чтобы создать сложную сцену-скетч.
2. Свяжите задержку с игровой логикой, например, запускайте анимацию поражения не по клику, а когда здоровье персонажа достигает нуля.
3. Используйте playAfterDelay() вместе с системой событий Phaser (this.events), чтобы уведомлять другие части игры о завершении отложенной анимации.
