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

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

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

        //  Just a few images to use in our underwater scene
        this.load.image('undersea', 'assets/pics/undersea.jpg');
        this.load.image('coral', 'assets/pics/seabed.png');
    }

    create ()
    {
        this.add.image(400, 300, 'undersea');

        //  Create the Animations
        //  These are stored globally, and can be used by any Sprite

        //  In the texture atlas the jellyfish uses the frame names blueJellyfish0000 to blueJellyfish0032
        //  So we can use the handy generateFrameNames function to create this for us (and so on)
        this.anims.create({ key: 'jellyfish', frames: this.anims.generateFrameNames('sea', { prefix: 'blueJellyfish', end: 32, zeroPad: 4 }), repeat: -1 });
        this.anims.create({ key: 'crab', frames: this.anims.generateFrameNames('sea', { prefix: 'crab1', end: 25, zeroPad: 4 }), repeat: -1 });
        this.anims.create({ key: 'octopus', frames: this.anims.generateFrameNames('sea', { prefix: 'octopus', end: 24, zeroPad: 4 }), repeat: -1 });
        this.anims.create({ key: 'purpleFish', frames: this.anims.generateFrameNames('sea', { prefix: 'purpleFish', end: 20, zeroPad: 4 }), repeat: -1 });
        this.anims.create({ key: 'stingray', frames: this.anims.generateFrameNames('sea', { prefix: 'stingray', end: 23, zeroPad: 4 }), repeat: -1 });

        const jellyfish = this.add.sprite(400, 300, 'seacreatures').play('jellyfish');
        const bigCrab = this.add.sprite(550, 480, 'seacreatures').setOrigin(0).play('crab');
        const smallCrab = this.add.sprite(730, 515, 'seacreatures').setScale(0.5).setOrigin(0).play('crab');
        const octopus = this.add.sprite(100, 100, 'seacreatures').play('octopus');
        const fish = this.add.sprite(600, 200, 'seacreatures').play('purpleFish');
        const ray = this.add.sprite(100, 300, 'seacreatures').play('stingray');

        this.add.image(0, 466, 'coral').setOrigin(0);
    }
}

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

const game = new Phaser.Game(config);

Подготовка ассетов: загрузка атласа и изображений

Вся подготовка данных происходит в методе preload. Ключевой момент — загрузка текстурного атласа с помощью метода this.load.atlas. Этот метод принимает три аргумента: уникальный ключ ассета, путь к изображению-атласу и путь к JSON-файлу с данными о координатах кадров (спрайтшит).

Дополнительно загружаются фоновые изображения для создания сцены.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.atlas('sea', 'assets/animations/seacreatures_json.png', 'assets/animations/seacreatures_json.json');

    this.load.image('undersea', 'assets/pics/undersea.jpg');
    this.load.image('coral', 'assets/pics/seabed.png');
}

Создание глобальных анимаций

Основная логика создания анимаций находится в методе create. Анимации создаются глобально через менеджер this.anims и могут быть использованы любым спрайтом в игре.

Для создания используется метод this.anims.create(), который принимает объект конфигурации. Самый важный параметр — frames. Вместо ручного перечисления всех кадров мы используем хелпер this.anims.generateFrameNames(). Этот метод анализирует загруженный атлас по ключу и генерирует массив кадров на основе переданных правил именования.

Рассмотрим параметры generateFrameNames для анимации медузы ('jellyfish'): - prefix: 'blueJellyfish': Общая часть имени каждого кадра в атласе. - end: 32: Номер последнего кадра в последовательности. - zeroPad: 4: Указывает, что номер кадра дополнен нулями до 4 знаков (например, '0000', '0001', ..., '0032').

Параметр repeat: -1 задает бесконечное повторение анимации.

create ()
{
    this.anims.create({ key: 'jellyfish', frames: this.anims.generateFrameNames('sea', { prefix: 'blueJellyfish', end: 32, zeroPad: 4 }), repeat: -1 });
    this.anims.create({ key: 'crab', frames: this.anims.generateFrameNames('sea', { prefix: 'crab1', end: 25, zeroPad: 4 }), repeat: -1 });
    this.anims.create({ key: 'octopus', frames: this.anims.generateFrameNames('sea', { prefix: 'octopus', end: 24, zeroPad: 4 }), repeat: -1 });
    this.anims.create({ key: 'purpleFish', frames: this.anims.generateFrameNames('sea', { prefix: 'purpleFish', end: 20, zeroPad: 4 }), repeat: -1 });
    this.anims.create({ key: 'stingray', frames: this.anims.generateFrameNames('sea', { prefix: 'stingray', end: 23, zeroPad: 4 }), repeat: -1 });
}

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

После того как анимации созданы, мы можем добавлять спрайты и назначать им анимации. Спрайты создаются с помощью this.add.sprite(), где указывается позиция (x, y) и ключ текстурного атласа ('sea').

Обратите внимание: спрайт использует атлас как источник текстур, но конкретный кадр при создании не важен, так как мы сразу запускаем анимацию методом .play() с ключом созданной ранее анимации (например, 'jellyfish').

Для краба также демонстрируются методы настройки отрисовки: .setOrigin(0) меняет точку привязки (anchor) на верхний левый угол, а .setScale(0.5) уменьшает размер спрайта вдвое.

const jellyfish = this.add.sprite(400, 300, 'sea').play('jellyfish');
const bigCrab = this.add.sprite(550, 480, 'sea').setOrigin(0).play('crab');
const smallCrab = this.add.sprite(730, 515, 'sea').setScale(0.5).setOrigin(0).play('crab');
const octopus = this.add.sprite(100, 100, 'sea').play('octopus');
const fish = this.add.sprite(600, 200, 'sea').play('purpleFish');
const ray = this.add.sprite(100, 300, 'sea').play('stingray');

this.add.image(0, 466, 'coral').setOrigin(0);

Конфигурация и запуск игры

Это стандартная конфигурация игры Phaser. Важно, что в поле scene передается наш класс Example. Игра создается экземпляром Phaser.Game с этой конфигурацией.

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

const game = new Phaser.Game(config);

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

Использование текстурных атласов и метода generateFrameNames — это эффективный и чистый способ работы с анимациями в Phaser. Он позволяет легко управлять большими наборами кадров и переиспользовать анимации между спрайтами. Для экспериментов попробуйте: 1. Изменить параметры repeat (например, на 3) или frameRate в объекте конфигурации анимации. 2. Создать анимацию, которая проигрывается только по клику на спрайт, используя метод .play() в обработчике события. 3. Использовать другой метод для создания кадров, например, generateFrameNumbers, если ваши кадры в атласе пронумерованы последовательно, но без префикса.