О чем этот пример

Управление анимациями — ключ к оживлению игровых миров. Phaser предоставляет мощный API для анимации спрайтов, выходящий за рамки простого проигрывания. Эта статья разбирает официальный пример, демонстрирующий тонкий контроль над воспроизведением: запуск в обратном порядке, режим «йо-йо», пауза и моментальная смена направления. Вы научитесь создавать более динамичные и интерактивные визуальные эффекты, управляя анимацией в реальном времени по запросу игрока.

Версия 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.spritesheet('mummy', 'assets/animations/mummy37x45.png', { frameWidth: 37, frameHeight: 45 });
    }

    create ()
    {
        //  Frame debug view
        this.frameView = this.add.graphics({ fillStyle: { color: 0xff00ff }, x: 32, y: 32 });

        //  Show the whole animation sheet
        this.add.image(32, 32, 'mummy', '__BASE').setOrigin(0);

        const config = {
            key: 'walk',
            frames: this.anims.generateFrameNumbers('mummy'),
            frameRate: 6,
            yoyo: false,
            repeat: -1
        };

        this.anim = this.anims.create(config);

        this.sprite = this.add.sprite(400, 250, 'mummy').setScale(4);

        //  Debug text
        this.progress = this.add.text(100, 420, 'Progress: 0%', { color: '#00ff00' });

        this.input.keyboard.on('keydown-SPACE', function (event) {
            this.sprite.play('walk');
        }, this);

        this.input.keyboard.on('keydown-Y', function (event) {
            this.sprite.anims.yoyo = !this.sprite.anims.yoyo;
        }, this);

        this.input.keyboard.on('keydown-Q', function (event) {
            this.sprite.playReverse('walk');
        }, this);

        this.input.keyboard.on('keydown-R', function (event) {
            this.sprite.anims.reverse();
        }, this);

        this.input.keyboard.on('keydown-P', function (event) {

            if (this.sprite.anims.isPaused)
            {
                this.sprite.anims.resume();
            }
            else
            {
                this.sprite.anims.pause();
            }
        }, this);
    }

    update ()
    {
        this.updateFrameView();

        const debug = [
            'SPACE to play the animation, Q to play reverse,',
            'R to revert at any time, P to pause/resume,',
            'Y to toggle yoyo',
            '',
            'Yoyo: ' + this.sprite.anims.yoyo,
            'Reverse: ' + this.sprite.anims.inReverse,
            'Progress: ' + this.sprite.anims.getProgress() * 100 + '%',
            'Accumulator: ' + this.sprite.anims.accumulator,
            'NextTick: ' + this.sprite.anims.nextTick
        ];

        this.progress.setText(debug);
    }

    updateFrameView ()
    {
        if (this.sprite.anims.isPlaying)
        {
            this.frameView.clear();
            this.frameView.fillRect(this.sprite.frame.cutX, 0, 37, 45);
        }
    }
}

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    backgroundColor: '#4d4d4d',
    pixelArt: true,
    scene: Example
};

const game = new Phaser.Game(config);

Создание и базовый запуск анимации

В методе create() сцена загружает спрайтшит и создает из него анимационную конфигурацию. Ключевой метод this.anims.generateFrameNumbers('mummy') автоматически генерирует массив кадров из загруженного листа спрайтов.

const config = {
    key: 'walk',
    frames: this.anims.generateFrameNumbers('mummy'),
    frameRate: 6,
    yoyo: false,
    repeat: -1
};
this.anim = this.anims.create(config);
this.sprite = this.add.sprite(400, 250, 'mummy').setScale(4);

Анимация 'walk' создается, но не запускается автоматически. Спрайт this.sprite масштабируется и готов к отображению. Запуск происходит по нажатию пробела, который вызывает метод play() для этого спрайта.

this.input.keyboard.on('keydown-SPACE', function (event) {
    this.sprite.play('walk');
}, this);

Запуск в обратном порядке и инверсия

Phaser предлагает два основных способа воспроизвести анимацию задом наперед: начать с конца или инвертировать текущее направление.

Метод playReverse() запускает анимацию с последнего кадра к первому. Это отдельная команда старта.

this.input.keyboard.on('keydown-Q', function (event) {
    this.sprite.playReverse('walk');
}, this);

Метод reverse() моментально инвертирует направление воспроизведения уже играющей анимации. Если она шла вперед, она пойдет назад, и наоборот. Это мощный инструмент для мгновенной реакции.

this.input.keyboard.on('keydown-R', function (event) {
    this.sprite.anims.reverse();
}, this);

Текущее направление можно проверить через свойство this.sprite.anims.inReverse.

Режим Yoyo и управление паузой

Режим «йо-йо» заставляет анимацию проигрываться до конца, а затем — в обратном порядке до начала, создавая цикл «туда-обратно». Его можно включить глобально для анимации в конфиге (yoyo: true) или переключать динамически для конкретного спрайта.

this.input.keyboard.on('keydown-Y', function (event) {
    this.sprite.anims.yoyo = !this.sprite.anims.yoyo;
}, this);

Управление паузой осуществляется через методы pause() и resume(). Свойство isPaused позволяет проверить текущее состояние.

this.input.keyboard.on('keydown-P', function (event) {
    if (this.sprite.anims.isPaused) {
        this.sprite.anims.resume();
    } else {
        this.sprite.anims.pause();
    }
}, this);

Отладка и мониторинг состояния

Пример активно использует отладочную информацию, которая обновляется каждый кадр в update(). Это демонстрирует ключевые свойства и методы для отслеживания состояния анимации.

const debug = [
    'Yoyo: ' + this.sprite.anims.yoyo,
    'Reverse: ' + this.sprite.anims.inReverse,
    'Progress: ' + this.sprite.anims.getProgress() * 100 + '%',
    'Accumulator: ' + this.sprite.anims.accumulator,
    'NextTick: ' + this.sprite.anims.nextTick
];
this.progress.setText(debug);

getProgress() возвращает прогресс анимации от 0 до 1. accumulator и nextTick — внутренние тайминговые счетчики системы анимации. Также в примере есть визуализация текущего кадра (updateFrameView), которая рисует поверх спрайтшита.

this.frameView.fillRect(this.sprite.frame.cutX, 0, 37, 45);

Что попробовать дальше

Используя playReverse(), reverse() и динамическое управление yoyo, вы можете создавать сложные паттерны движения без подготовки отдельных анимаций. Экспериментируйте: свяжите инверсию анимации с поворотом спрайта для реалистичного разворота персонажа, используйте yoyo для пульсирующих эффектов UI, а комбинацию паузы и reverse — для создания эффектов «отскока» или перемотки времени. Контроль над анимацией — это контроль над ощущениями от игры.