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

При создании 2D-игр часто требуется управлять множеством анимаций. Phaser 3 предлагает мощный и гибкий способ загрузки данных об анимациях — отдельно от атласов текстур. Это улучшает организацию кода, позволяет переиспользовать одни и те же анимации для разных наборов спрайтов и упрощает работу художникам, которые могут редактировать JSON-файлы независимо. В этой статье мы разберем, как загружать анимации из внешнего JSON-файла и применять их к спрайтам из Texture Atlas.

Версия 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.animation('gemData', 'assets/animations/gems.json');
        this.load.atlas('gems', 'assets/tests/columns/gems.png', 'assets/tests/columns/gems.json');
    }

    create ()
    {
        this.add.sprite(400, 100, 'gems').play('diamond');
        this.add.sprite(400, 200, 'gems').play('prism');
        this.add.sprite(400, 300, 'gems').play('ruby');
        this.add.sprite(400, 400, 'gems').play('square');
    }
}

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

const game = new Phaser.Game(config);

Зачем нужна отдельная загрузка анимаций?

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

1. **Декомпозировать данные:** Хранить конфигурацию анимаций (кадры, частоту, повторение) отдельно от логики игры. 2. **Упростить контент-менеджмент:** Художники или геймдизайнеры могут редактировать JSON-файлы, не касаясь кода. 3. **Переиспользовать анимации:** Один JSON-файл с анимациями можно применять к разным атласам спрайтов, если в них одинаковая структура кадров.

В примере мы загружаем файл gems.json, который содержит определения нескольких анимаций для драгоценных камней.

Настройка загрузки в методе preload

Ключевой этап — загрузка ресурсов. Мы используем два метода загрузчика (this.load): один для данных анимации, другой для текстур.

this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.animation('gemData', 'assets/animations/gems.json');
this.load.atlas('gems', 'assets/tests/columns/gems.png', 'assets/tests/columns/gems.json');

1. setBaseURL задает базовый URL для всех последующих загрузок, что избавляет от необходимости прописывать полные пути. 2. load.animation('gemData', ...) загружает JSON-файл с конфигурацией анимаций и регистрирует его в менеджере анимаций под ключом 'gemData'. Важно: это именно **ключ данных**, а не ключ текстуры. 3. load.atlas('gems', ...) загружает атлас текстур — изображение (gems.png) и файл с координатами кадров (gems.json). Он регистрирует текстуру под ключом 'gems'.

После выполнения preload Phaser автоматически создаст все анимации, описанные в файле assets/animations/gems.json.

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

В методе create, когда все ресурсы загружены, мы создаем спрайты и запускаем на них анимации. Код очень лаконичен, так как вся сложность описания анимаций уже скрыта в JSON-файле.

this.add.sprite(400, 100, 'gems').play('diamond');
this.add.sprite(400, 200, 'gems').play('prism');
this.add.sprite(400, 300, 'gems').play('ruby');
this.add.sprite(400, 400, 'gems').play('square');
1.  `this.add.sprite(x, y, 'gems')` создает спрайт в указанных координатах, используя текстуру из атласа с ключом `'gems'`.
2.  Метод `.play('diamond')` ищет в глобальном менеджере анимаций (`this.anims`) анимацию с ключом `'diamond'`. Эта анимация была автоматически создана и зарегистрирована при загрузке файла `gemData`. Phaser сопоставляет кадры анимации с кадрами из атласа `'gems'` на основе их названий, указанных в JSON.

Таким образом, спрайт «знает», какую текстуру использовать для отрисовки, а менеджер анимаций «знает», в какой последовательности и с какой скоростью эти текстуры менять.

Структура конфигурационного JSON-файла

Чтобы метод работал корректно, JSON-файл должен следовать определенному формату, который ожидает Phaser. Вот пример структуры для одной анимации:

on
{
  "anims": [
    {
      "key": "diamond",
      "frames": [
        { "key": "gems", "frame": "diamond_01" },
        { "key": "gems", "frame": "diamond_02" },
        { "key": "gems", "frame": "diamond_03" }
      ],
      "frameRate": 12,
      "repeat": -1
    }
  ]
}

* key: Уникальный ключ анимации, который используется в методе .play(). * frames: Массив объектов, каждый из которых ссылается на конкретный кадр. Поле key внутри каждого кадра — это ключ текстуры ('gems'), а frame — имя конкретного фрейма внутри атласа (должно соответствовать именам в gems.json). * frameRate: Скорость воспроизведения в кадрах в секунду. * repeat: Количество повторений (-1 для бесконечного цикла).

В одном файле anims может быть массив с множеством таких определений анимаций.

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

Использование load.animation() для загрузки анимаций из JSON — это профессиональный подход к структурированию игровых данных в Phaser 3. Он разделяет ответственность: код отвечает за логику, а внешние файлы — за контент. Для экспериментов попробуйте: 1. Создать один JSON-файл анимаций для всех персонажей игры. 2. Применить одну и ту же анимацию (например, 'idle') к спрайтам из разных атласов, если имена кадров в них совпадают. 3. Динамически загружать разные JSON-файлы с анимациями для разных уровней игры.