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

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

Версия 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.image('rick', 'assets/sprites/rick.png');
    }

    create ()
    {
        //  Our container
        const container = this.add.container(400, 300);

        //  Create some sprites - positions are relative to the Container x/y
        const sprite0 = this.add.sprite(-400, 0, 'rick');
        const sprite1 = this.add.sprite(0, 0, 'rick');
        const sprite2 = this.add.sprite(400, 0, 'rick');
        const sprite3 = this.add.sprite(-200, -200, 'rick');
        const sprite4 = this.add.sprite(200, -200, 'rick');
        const sprite5 = this.add.sprite(200, 200, 'rick');
        const sprite6 = this.add.sprite(-200, 200, 'rick');

        container.add([ sprite0, sprite1, sprite2, sprite3, sprite4, sprite5, sprite6 ]);

        this.tweens.add({
            targets: container,
            angle: { value: 360, duration: 6000 },
            scaleX: { value: 0.5, duration: 3000, yoyo: true, ease: 'Quad.easeInOut' },
            scaleY: { value: 0.5, duration: 3000, yoyo: true, ease: 'Quad.easeInOut' },
            repeat: -1
        });
    }
}

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

const game = new Phaser.Game(config);

Создание и наполнение контейнера

Контейнер (Container) – это особый тип игрового объекта в Phaser, который сам не имеет графического представления, но может выступать в качестве родителя для других объектов. Все дочерние объекты наследуют трансформации (позицию, вращение, масштаб) контейнера.

Сначала создается сам контейнер с указанием его начальных мировых координат.

const container = this.add.container(400, 300);

Затем создаются спрайты. Важный нюанс: координаты спрайтов задаются не относительно мировых координат сцены, а относительно точки (`x,y`) самого контейнера.

const sprite1 = this.add.sprite(0, 0, 'rick');
const sprite2 = this.add.sprite(400, 0, 'rick');

Чтобы добавить спрайты в контейнер, используется метод add(). В него можно передать как массив объектов, так и отдельные аргументы.

container.add([ sprite0, sprite1, sprite2, sprite3, sprite4, sprite5, sprite6 ]);

Относительные координаты дочерних объектов

Ключевое преимущество контейнера – система координат. После добавления спрайта в контейнер его свойства `xиyстановятся относительными к позиции контейнера. Например, спрайт с координатами(0, 0)` будет отрисован прямо в центре контейнера.

В нашем примере спрайты размещаются по кругу и крестом относительно точки (400, 300) на сцене, потому что именно там создан контейнер. Если изменить позицию контейнера с помощью твина или прямого присваивания, все его дочерние спрайты синхронно сместятся вместе с ним, сохраняя свои относительные позиции внутри группы.

Это избавляет разработчика от необходимости вручную обновлять позиции каждого объекта в группе при их общем перемещении.

Применение анимации к контейнеру

Настоящая сила контейнеров раскрывается при работе с анимациями системы Tweens. Вместо того чтобы создавать твин для каждого спрайта в отдельности, можно создать один твин, нацеленный на контейнер.

В примере твин одновременно меняет три свойства контейнера: угол поворота и масштаб по обеим осям.

this.tweens.add({
    targets: container,
    angle: { value: 360, duration: 6000 },
    scaleX: { value: 0.5, duration: 3000, yoyo: true, ease: 'Quad.easeInOut' },
    scaleY: { value: 0.5, duration: 3000, yoyo: true, ease: 'Quad.easeInOut' },
    repeat: -1
});

Параметр targets указывает, к какому объекту (нашему контейнеру) применяется анимация. Свойства angle, scaleX и scaleY анимируются с разной длительностью и эффектами. Например, yoyo: true заставляет анимацию масштаба проигрываться в обратном порядке после завершения, создавая эффект "пульсации". Все дочерние спрайты автоматически унаследуют эти трансформации, создавая синхронизированный визуальный эффект для всей группы.

Структура проекта и конфигурация

Пример представляет собой законченную сцену (Scene). В методе preload() загружается один спрайт.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('rick', 'assets/sprites/rick.png');
}

Основная логика находится в create(), где происходят все описанные операции: создание контейнера, спрайтов и анимации.

Конфигурация игры (config) стандартна: указывается тип рендерера, размеры холста, цвет фона и класс основной сцены.

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

Инициализация игры выполняется созданием экземпляра Phaser.Game с этой конфигурацией.

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

Контейнеры в Phaser – это фундаментальный инструмент для структурирования сложных игровых объектов. Они позволяют управлять группами спрайтов, текстов, частиц или других контейнеров как единым целым, что критически важно для создания иерархических сцен (например, персонаж с оружием в руке) и сложных анимаций. **Идеи для экспериментов:** 1. Попробуйте вложить один контейнер в другой и применить анимации к родительскому контейнеру. 2. Добавьте в контейнер не только спрайты, но и текст (this.add.text) или графические примитивы (this.add.rectangle). 3. Используйте свойство setDepth контейнера, чтобы управлять порядком отрисовки всей группы объектов относительно других элементов сцены. 4. Поэкспериментируйте с изменением точки происхождения (originX, originY) контейнера и посмотрите, как это повлияет на вращение и масштабирование всей группы.