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

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

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

Живой запуск

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

Исходный код


let spineboy
let container

class Example extends Phaser.Scene
{
    constructor ()
    {
        super({
            pack: {
                files: [
                    { type: 'scenePlugin', key: 'SpinePlugin', url: 'plugins/3.8.95/SpinePluginDebug.js', sceneKey: 'spine' }
                ]
            }
        });
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('logo', 'assets/sprites/phaser.png');
        this.load.setPath('assets/spine/3.8/demos/');
        this.load.spine('set1', 'demos.json', [ 'atlas1.atlas' ], true);
    }

    create ()
    {
        this.add.image(0, 0, 'logo').setOrigin(0);
        container = this.add.spineContainer()
        spineboy = this.add.spine(400, 600, 'set1.spineboy', 'idle', true);
        container.add(spineboy)

        container.setScale(0.5)
    }

    update(time, delta)
    {
        this.cameras.main.setZoom(Math.sin(time / 1000));
    }
}

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

const game = new Phaser.Game(config);

Подготовка и загрузка ассетов

В Phaser 3 работа со Spine-анимациями требует предварительной настройки плагина и корректной загрузки файлов. В конструкторе сцены указывается загрузка SpinePlugin, который обеспечивает поддержку формата Spine.

constructor ()
{
    super({
        pack: {
            files: [
                { type: 'scenePlugin', key: 'SpinePlugin', url: 'plugins/3.8.95/SpinePluginDebug.js', sceneKey: 'spine' }
            ]
        }
    });
}

В методе preload задаются базовые пути и загружаются необходимые ресурсы: спрайт для фона и Spine-ассет. Обратите внимание, что для Spine указывается ключ set1, JSON-файл с данными анимации (demos.json), атлас текстур (atlas1.atlas) и флаг true для загрузки в формате JSON.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('logo', 'assets/sprites/phaser.png');
    this.load.setPath('assets/spine/3.8/demos/');
    this.load.spine('set1', 'demos.json', [ 'atlas1.atlas' ], true);
}

Создание контейнера и Spine-объекта

В методе create размещаются объекты на сцене. Сначала добавляется фоновое изображение с логотипом Phaser, затем создается специальный контейнер для Spine-объектов с помощью this.add.spineContainer(). Этот контейнер позволяет группировать несколько Spine-персонажей и применять к ним общие трансформации.

create ()
{
    this.add.image(0, 0, 'logo').setOrigin(0);
    container = this.add.spineContainer()
    spineboy = this.add.spine(400, 600, 'set1.spineboy', 'idle', true);
    container.add(spineboy)

    container.setScale(0.5)
}

Spine-персонаж создается через this.add.spine(), где задаются координаты (400, 600), ключ ассета (set1.spineboy), начальная анимация (idle) и флаг автоматического воспроизведения (true). После создания объект добавляется в контейнер, и ко всему контейнеру применяется масштабирование в 0.5 раза. Это уменьшает и персонажа, и любые другие объекты внутри контейнера.

Динамическое управление камерой

Метод update вызывается на каждом кадре игры и здесь используется для создания пульсирующего эффекта зумирования камеры. Значение зума вычисляется на основе синуса от текущего игрового времени, что дает плавное циклическое изменение.

update(time, delta)
{
    this.cameras.main.setZoom(Math.sin(time / 1000));
}

this.cameras.main ссылается на основную камеру сцены. Метод setZoom() изменяет масштаб отображения всей сцены. Использование Math.sin(time / 1000) гарантирует, что зум будет колебаться между -1 и 1 с периодом около 6.28 секунд (2 * π * 1000 мс). Визуально это создает эффект "дыхания" или пульсации всей сцены, включая Spine-персонажа и фон.

Конфигурация игры и инициализация

Ключевой момент — конфигурация рендерера. В примере явно указан Phaser.CANVAS, что важно для корректной работы Spine в некоторых окружениях, особенно при отладке или специфичных настройках производительности.

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

После определения конфигурации создается экземпляр игры. Это запускает весь жизненный цикл Phaser: инициализацию, загрузку ресурсов, создание сцены и начало игрового цикла с вызовами update.

const game = new Phaser.Game(config);

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

Этот пример демонстрирует базовое использование Spine-анимаций в Phaser 3 с группировкой через контейнеры и динамическим управлением камерой. Для экспериментов попробуйте: добавить в контейнер несколько Spine-объектов с разными анимациями, изменить формулу зумирования камеры на более сложную (например, с использованием Math.cos или комбинации функций), или применить другие трансформации к контейнеру (вращение, смещение). Также можно поэкспериментировать с переключением анимаций персонажа по клику или таймеру, используя методы объекта spineboy.