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

Импорт анимаций из Aseprite — мощный способ оживить вашего игрового персонажа, избегая ручного создания кадров в коде. В этом примере показана базовая загрузка двух спрайтшитов (головы и тела) и автоматическое создание анимационных таймлайнов на основе тегов, экспортированных из Aseprite. Это экономит часы работы и позволяет дизайнерам напрямую влиять на игровой процесс.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.path = 'assets/bugs/';

        this.load.aseprite('Head', 'MainChrHead.png', 'MainChrHead.json');
        this.load.aseprite('Body', 'MainChr.png', 'MainChr.json');
    }

    create ()
    {
        this.add.sprite(100, 100, 'Head', '0').setScale(4);
        this.add.sprite(300, 100, 'Body', '0').setScale(4);

        // console.log(this.cache.json.get('Head'));
        // console.log(this.cache.json.get('Body'));

        const BodyTags = this.anims.createFromAseprite("Body");
        const HeadTags = this.anims.createFromAseprite("Head");

        // console.log(BodyTags[0].frames[0], HeadTags[0].frames[0]);
        // console.log(BodyTags, HeadTags);

        // const tags = this.anims.createFromAseprite('paladin');
        // console.log(tags);

        // const step = this.anims.createFromAseprite('paladin', 'step');
        // const delay = this.anims.createFromAseprite('paladin', 'Delay');
        // const release = this.anims.createFromAseprite('paladin', 'release');

        // console.log(step);
        // console.log(delay);
        // console.log(release);

        // const sprite = this.add.sprite(500, 300).play({ key: 'step', repeat: -1 }).setScale(6);
    }
}

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

const game = new Phaser.Game(config);

Загрузка Aseprite-ассетов

Метод preload() сцены отвечает за загрузку всех необходимых ресурсов. Для работы с Aseprite файлами используется специальный загрузчик load.aseprite().

this.load.aseprite('Head', 'MainChrHead.png', 'MainChrHead.json');
this.load.aseprite('Body', 'MainChr.png', 'MainChr.json');

Первый аргумент — это ключ (key), под которым ассет будет доступен в кеше. Второй и третий аргументы — это пути к PNG-изображению и JSON-файлу с метаданными (размерами кадров и тегами анимаций), которые экспортирует Aseprite. Важно предварительно настроить базовый URL и путь, чтобы не указывать полные адреса для каждого файла.

Создание статичных спрайтов

В методе create() сначала создаются два простых спрайта для визуальной проверки загруженных текстур.

this.add.sprite(100, 100, 'Head', '0').setScale(4);
this.add.sprite(300, 100, 'Body', '0').setScale(4);

Первый аргумент add.sprite() — координаты X и Y. Второй — ключ текстуры (например, 'Head'). Третий, необязательный аргумент — это индекс или имя кадра. Здесь '0' указывает на самый первый кадр из загруженного спрайтшита. Метод setScale(4) увеличивает спрайт в 4 раза для наглядности. На этом этапе спрайты статичны.

Магия createFromAseprite

Ключевой метод для работы с анимациями — this.anims.createFromAseprite(). Он парсит JSON-данные, загруженные из Aseprite, и автоматически создает в менеджере анимаций (this.anims) готовые анимации на основе тегов (tags).

const BodyTags = this.anims.createFromAseprite("Body");
const HeadTags = this.anims.createFromAseprite("Head");

Если вызвать метод только с ключом текстуры (как в примере), он создаст анимации для ВСЕХ тегов, найденных в JSON-файле этого ассета. Метод возвращает массив созданных анимаций. Каждая анимация получает ключ (key), совпадающий с именем тега из Aseprite. Эти анимации теперь можно использовать в любом спрайте, используя sprite.play('имя_тега').

Как это работает под капотом

Phaser загружает JSON-файл от Aseprite в кеш (this.cache.json). Этот файл содержит массив frames с координатами каждого кадра на спрайтшите и массив meta.frameTags с именами анимаций (тегами) и диапазонами кадров.

// Для отладки можно посмотреть структуру данных:
// console.log(this.cache.json.get('Head'));

Метод createFromAseprite проходит по массиву frameTags и для каждого тега регистрирует новую анимацию, используя кадры из указанного диапазона. Все параметры анимации (задержки между кадрами) берутся из данных Aseprite. Это избавляет разработчика от ручного описания каждого кадра анимации в коде игры.

Расширенное использование: создание конкретных анимаций

Метод createFromAseprite может создавать не все анимации разом, а только конкретные, указанные по имени тега. Это полезно для оптимизации или выборочной загрузки.

// Пример из закомментированного кода:
// const step = this.anims.createFromAseprite('paladin', 'step');

Вторым аргументом можно передать строку с именем одного тега или массив строк с именами нескольких тегов. Метод вернет массив созданных анимаций (в случае одного тега — массив из одного элемента). Это дает точный контроль над тем, какие анимации будут зарегистрированы в системе.

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

Использование createFromAseprite кардинально упрощает pipeline «художник → игра». Все настройки анимации остаются в редакторе. Для экспериментов попробуйте

  1. Запустить анимацию на одном из созданных спрайтов, используя sprite.play('имя_тега')
  2. Экспортировать из Aseprite анимацию с разной длительностью кадров и убедиться, что Phaser корректно их учитывает
  3. Загрузить спрайтшит, содержащий несколько персонажей (атки, айдлы) в разных тегах, и переключать их в зависимости от действий игрока