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

В Phaser анимации спрайтов часто управляются автоматически, но для создания плавных UI-индикаторов, синхронизации событий или нелинейных скриптовых последовательностей нужно точно знать, на каком этапе воспроизведения находится клип. Метод `anims.getProgress()` предоставляет нормализованное значение от 0 до 1, отражающее текущий прогресс анимации, независимо от её длины или сложности. В этой статье разберём, как использовать этот метод для визуализации прогресса и интеграции его в игровую логику.

Версия 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('gems', 'assets/animations/diamond.png', 'assets/animations/diamond.json');
    }

    create ()
    {
        this.anims.create({
            key: 'diamond',
            frames: this.anims.generateFrameNames('gems', { prefix: 'diamond_', end: 15, zeroPad: 4 }),
            frameRate: 16,
            yoyo: true,
            repeat: -1,
            repeatDelay: 300
        });

        // this.gem is a local variable.
        this.gem = this.add.sprite(400, 480, 'gems')
            .play('diamond').setScale(4);

        this.debug = this.add.graphics();
    }

    update ()
    {
        this.debug.clear();

        const size = 672;

        this.debug.fillStyle(0x2d2d2d);
        this.debug.fillRect(64, 64, size, 48);

        this.debug.fillStyle(0x2dff2d);
        this.debug.fillRect(64, 64, size * this.gem.anims.getProgress(), 48);
    }
}

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

const game = new Phaser.Game(config);

Подготовка анимации и спрайта

В первую очередь необходимо создать и запустить анимацию на спрайте. В примере используется атлас gems с кадрами, названными по шаблону.

this.anims.create({
    key: 'diamond',
    frames: this.anims.generateFrameNames('gems', { prefix: 'diamond_', end: 15, zeroPad: 4 }),
    frameRate: 16,
    yoyo: true,
    repeat: -1,
    repeatDelay: 300
});

this.gem = this.add.sprite(400, 480, 'gems')
    .play('diamond').setScale(4);

Анимация diamond создаётся с помощью this.anims.create. Параметр yoyo: true заставляет анимацию проигрываться в обратном порядке после завершения прямого цикла, repeat: -1 задаёт бесконечное повторение, а repeatDelay: 300 добавляет задержку в 300 миллисекунд между повторами. Спрайт this.gem создаётся в позиции (400, 480), ему назначается анимация diamond, и он масштабируется в 4 раза для наглядности.

Получение прогресса анимации

Ключевой метод для отслеживания — anims.getProgress(), вызываемый у компонента анимации спрайта. Он возвращает число от 0 (начало анимации) до 1 (её завершение).

const progress = this.gem.anims.getProgress();

Это значение обновляется каждый кадр, даже если анимация зациклена или использует эффект yoyo. Для анимаций с repeat: -1 прогресс будет циклически меняться от 0 до 1. Важно: getProgress() всегда возвращает прогресс текущего цикла, а не всей последовательности повторов.

Визуализация прогресса через графику

В методе update() каждый кадр очищается и перерисовывается индикатор прогресса. Используется графика this.debug для отрисовки двух прямоугольников: фона и заполняемой части.

update ()
{
    this.debug.clear();

    const size = 672;

    this.debug.fillStyle(0x2d2d2d);
    this.debug.fillRect(64, 64, size, 48);

    this.debug.fillStyle(0x2dff2d);
    this.debug.fillRect(64, 64, size * this.gem.anims.getProgress(), 48);
}

Сначала this.debug.clear() удаляет предыдущую графику. Затем рисуется тёмно-серый фон (0x2d2d2d) фиксированного размера. Поверх него рисуется зелёный (0x2dff2d) прямоугольник, ширина которого вычисляется как size * this.gem.anims.getProgress(). Таким образом, зелёная полоса расширяется и сжимается в точном соответствии с прогрессом анимации алмаза.

Практическое применение в играх

Отслеживание прогресса анимации полезно не только для дебага. Вот несколько реальных сценариев:

- **Синхронизация звуков или эффектов**: воспроизводить звук шага, когда прогресс анимации ходьбы достигает 0.5. - **Прогресс-бары заклинаний**: визуализировать длительность каста способности. - **Плавное управление камерой**: привязать движение камеры к прогрессу анимации перехода между локациями. - **Событийные триггеры**: выполнять код (например, наносить урон) в определённый момент анимации атаки.

if (this.player.anims.getProgress() > 0.7 && !this.attackTriggered) {
    this.applyDamage();
    this.attackTriggered = true;
}

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

Метод anims.getProgress() — это простой и мощный инструмент для связи визуальной анимации с игровой логикой. Он позволяет создавать отзывчивые интерфейсы и сложные последовательности действий. Попробуйте экспериментировать: привяжите прогресс анимации к изменению прозрачности спрайта, управлению частицами или генерации процедурной анимации для UI-элементов.