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

Создание плавной анимации из спрайтшита — основа визуальной динамики в играх. Phaser предлагает несколько способов указать, какие кадры использовать, от автоматического выбора всех кадров до тонкой настройки последовательности. В этой статье мы разберем три подхода на примере анимации взрыва, чтобы вы могли выбрать оптимальный для своего проекта и контролировать каждый кадр анимации.

Версия 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('boom', 'assets/sprites/explosion.png', { frameWidth: 64, frameHeight: 64, endFrame: 23 });
    }

    create ()
    {
        this.add.text(400, 32, 'Check the source code for comments', { color: '#00ff00' }).setOrigin(0.5, 0);

        //  Our 'boom' spritesheet has 23 frames in it.
        //  This animation will use them all by just giving it the sprite sheet key:
        const config1 = {
            key: 'explode1',
            frames: 'boom',
            frameRate: 20,
            repeat: -1
        };

        //  Here we use the 'generateFrameNumbers' function instead to set the start and end frame:
        const config2 = {
            key: 'explode2',
            frames: this.anims.generateFrameNumbers('boom', { start: 0, end: 23 }),
            frameRate: 20,
            repeat: -1
        };

        //  Here we use the 'frames' array because we want to specify the exact frames to use:
        const config3 = {
            key: 'explode3',
            frames: this.anims.generateFrameNumbers('boom', { frames: [ 0, 1, 2, 1, 2, 3, 4, 0, 1, 2 ] }),
            frameRate: 20,
            repeat: -1
        };

        this.anims.create(config1);
        this.anims.create(config2);
        this.anims.create(config3);

        this.add.sprite(200, 300, 'boom').play('explode1');
        this.add.sprite(400, 300, 'boom').play('explode2');
        this.add.sprite(600, 300, 'boom').play('explode3');
    }
}

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

const game = new Phaser.Game(config);

Загрузка спрайтшита: подготовка данных

Перед созданием анимации необходимо загрузить спрайтшит — изображение, содержащее все кадры. Ключевой метод load.spritesheet принимает ключ для идентификации, путь к файлу и объект конфигурации.

this.load.spritesheet('boom', 'assets/sprites/explosion.png', { frameWidth: 64, frameHeight: 64, endFrame: 23 });

В этом примере мы задали ключ 'boom', ширину и высоту каждого кадра (64x64), а также явно указали последний кадр (endFrame: 23). Phaser автоматически "нарежет" изображение на 24 кадра (от 0 до 23). Указание endFrame помогает движку, если количество кадров неочевидно.

Подход 1: Простая анимация из всего спрайтшита

Самый простой способ создать анимацию — передать ключ спрайтшита напрямую в свойство frames конфигурационного объекта. Phaser сам сгенерирует номера всех кадров, на которые был разрезан спрайтшит при загрузке.

const config1 = {
    key: 'explode1',
    frames: 'boom',
    frameRate: 20,
    repeat: -1
};

Здесь key — это уникальное имя анимации для последующего воспроизведения. Свойство frames содержит строку 'boom', что является инструкцией для Phaser: "возьми все кадры из спрайтшита с этим ключом". frameRate задает скорость проигрывания (20 кадров в секунду), а repeat: -1 делает анимацию зацикленной бесконечно. Этот метод идеален, когда нужно проиграть все кадры подряд без исключений.

Подход 2: Указание диапазона кадров через generateFrameNumbers

Для большего контроля используйте метод this.anims.generateFrameNumbers(). Он позволяет явно задать начальный и конечный кадры для анимации.

const config2 = {
    key: 'explode2',
    frames: this.anims.generateFrameNumbers('boom', { start: 0, end: 23 }),
    frameRate: 20,
    repeat: -1
};

Метод generateFrameNumbers первым аргументом принимает ключ спрайтшита, а вторым — объект опций. В данном случае опции start и end определяют диапазон. Результатом вызова метода является массив объектов-кадров, который помещается в свойство frames. Этот подход полезен, если ваш спрайтшит содержит несколько анимаций в одном ряду или вам нужно проиграть только часть кадров.

Подход 3: Произвольная последовательность кадров

Самый гибкий вариант — задать массив с конкретными номерами кадров в нужном порядке. Это делается с помощью опции frames внутри вызова generateFrameNumbers.

const config3 = {
    key: 'explode3',
    frames: this.anims.generateFrameNumbers('boom', { frames: [ 0, 1, 2, 1, 2, 3, 4, 0, 1, 2 ] }),
    frameRate: 20,
    repeat: -1
};

Здесь мы передаем массив [0, 1, 2, 1, 2, 3, 4, 0, 1, 2]. Phaser создаст анимацию, которая проигрывает кадры именно в этой последовательности. Это открывает возможности для нелинейных анимаций: можно создать эффект "дрожания" (повторяя кадры 1 и 2), вернуться к началу в середине последовательности или вовсе пропустить многие кадры.

Создание и воспроизведение анимаций

После настройки конфигурационных объектов их необходимо зарегистрировать в менеджере анимаций сцены и применить к спрайтам.

this.anims.create(config1);
this.anims.create(config2);
this.anims.create(config3);

this.add.sprite(200, 300, 'boom').play('explode1');
this.add.sprite(400, 300, 'boom').play('explode2');
this.add.sprite(600, 300, 'boom').play('explode3');

Метод this.anims.create() регистрирует анимацию в глобальном хранилище сцены по ключу, указанному в конфиге. После этого любой спрайт может её проиграть. В примере мы создаем три спрайта с помощью this.add.sprite(), передавая начальный ключ текстуры ('boom'), а затем для каждого вызываем метод .play() с ключом нужной анимации ('explode1', 'explode2', 'explode3').

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

Phaser предоставляет три уровня контроля над анимацией: от автоматического использования всех кадров до программирования сложных последовательностей. Для быстрого прототипирования используйте передачу ключа спрайтшита. Для стандартных анимаций — задавайте диапазон start и end. Для уникальных эффектов, вроде мерцания или возврата назад, составляйте массив frames. Экспериментируйте: попробуйте создать анимацию дыхания персонажа, циклически повторяя 2-3 кадра, или эффект телепортации, проигрывая кадры в обратном порядке.