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

При разработке игр часто возникает необходимость менять анимации персонажей или объектов в ответ на действия игрока. Например, герой может найти новую способность, и его анимация атаки должна стать длиннее и зрелищнее. Жёстко заданные наборы кадров в таких случаях ограничивают геймдизайн. В этой статье мы разберем, как динамически добавлять кадры в уже существующую и работающую анимацию в Phaser 3, используя метод `addFrame`. Этот подход полезен для создания интерактивных анимационных систем, реактивных визуальных эффектов и экономии ресурсов, позволяя не создавать множество почти идентичных анимаций заранее.

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

    create ()
    {
        const text = this.add.text(400, 32, 'Click to add frames to current Animation', { color: '#00ff00' })
            .setOrigin(0.5, 0);

        //  Create an animation with 5 frames
        this.anims.create({ key: 'diamond', frames: this.anims.generateFrameNames('gems', { prefix: 'diamond_', end: 15, zeroPad: 4 }), repeat: -1 });

        const group = this.add.group({
            key: 'gems',
            frame: 'diamond_0000',
            frameQuantity: 6 * 6
        });

        group.playAnimation('diamond');

        Phaser.Actions.GridAlign(group.getChildren(), {
            width: 6,
            height: 6,
            cellWidth: 64,
            cellHeight: 64,
            x: 240,
            y: 160
        });

        this.input.once('pointerup', function () {

            const diamond = this.anims.get('diamond');

            //  Add in the new frames to the current animation
            const newFrames = this.anims.generateFrameNames('gems', { prefix: 'square_', end: 14, zeroPad: 4 });

            diamond.addFrame(newFrames);

        }, this);
    }
}

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

const game = new Phaser.Game(config);

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

В начале сцены, в методе create, создаётся базовая анимация с именем 'diamond'. Для её генерации используется метод this.anims.generateFrameNames, который автоматически создаёт массив кадров из атласа 'gems' по заданному шаблону.

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

Ключевые параметры generateFrameNames: * prefix: 'diamond_' — имена кадров начинаются с этого префикса. * end: 15 — последний номер кадра в последовательности. * zeroPad: 4 — номера кадров дополняются нулями до четырёх знаков (например, diamond_0015).

Затем создаётся группа из 36 спрайтов, каждый из которых использует первый кадр анимации. Всем спрайтам группы сразу же проигрывается созданная анимация 'diamond'.

const group = this.add.group({
    key: 'gems',
    frame: 'diamond_0000',
    frameQuantity: 6 * 6
});
group.playAnimation('diamond');

Спрайты выравниваются в сетку 6x6 с помощью Phaser.Actions.GridAlign для наглядного отображения.

Получение объекта анимации и генерация новых кадров

Механика добавления кадров срабатывает по клику мыши. Сначала необходимо получить объект существующей анимации по её ключу. Это делается с помощью метода this.anims.get.

const diamond = this.anims.get('diamond');

Переменная diamond теперь содержит объект анимации типа Phaser.Animations.Animation, который позволяет управлять её свойствами.

Далее, для добавления в анимацию, нужен массив новых кадров. Мы снова используем generateFrameNames, но уже с другим префиксом, чтобы взять кадры с изображениями другого типа драгоценного камня из того же атласа.

const newFrames = this.anims.generateFrameNames('gems', { prefix: 'square_', end: 14, zeroPad: 4 });

В результате newFrames — это массив объектов, описывающих кадры square_0000square_0014.

Ключевой метод: добавление кадров в работающую анимацию

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

diamond.addFrame(newFrames);

Что происходит после этого вызова? 1. **Анимация расширяется**: все спрайты, которые в данный момент проигрывают анимацию 'diamond', автоматически получают обновлённую последовательность кадров. 2. **Воспроизведение продолжается**: если анимация была на цикле (repeat: -1), она продолжит проигрываться, и после оригинальных кадров с diamond_ начнут отображаться новые кадры с square_. Это создаёт эффект плавной трансформации или комбинированной анимации. 3. **Изменения применяются немедленно**: не требуется перезапускать анимацию или обновлять спрайты вручную. Система анимаций Phaser управляет этим автоматически.

Практические аспекты и особенности использования

Метод addFrame мощный, но требует понимания его поведения.

* **Порядок добавления**: Кадры добавляются в конец текущего списка. Если нужно вставить кадры в середину или начало, потребуется более сложная логика с полной перестройкой анимации. * **Тип кадров**: Метод принимает массив объектов кадров. Можно добавлять кадры, сгенерированные из атласа (generateFrameNames), из списка строк с именами кадров, или созданные вручную. * **Влияние на время**: Добавление кадров не сбрасывает текущий прогресс анимации. Спрайт продолжает проигрывание с той же временной позиции, но в более длинной последовательности. Это может быть использовано для создания "бесшовных" переходов. * **Управление через события**: В примере добавление инициируется кликом, но триггером может быть любое событие игры: сбор предмета, достижение уровня, нажатие клавиши.

Важно помнить, что исходный код анимации, заданный в create, не изменяется. Изменяется только её runtime-объект.

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

Динамическое добавление кадров в анимацию с помощью addFrame открывает путь к созданию более живых и отзывчивых игровых миров. Вместо предопределённых наборов анимаций вы можете собирать их подобно конструктору, реагируя на действия игрока. Для экспериментов попробуйте: 1. Добавлять кадры не по клику, а при столкновении спрайтов. 2. Создать систему комбо-атак, где каждый следующий удар удлиняет анимацию эффекта. 3. Реализовать "эволюцию" персонажа, где с ростом уровня его idle-анимация становится детализированнее за счёт добавленных кадров. 4. Добавлять разные наборы кадров в зависимости от выбранного игроком оружия или навыка.