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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    container;

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

    create ()
    {
        //  Here we've got 1 of each game object:
        const image = this.add.image(0, 0, 'lemming');
        const text = this.add.text(60, 0, 'Oh No!', { font: '16px Courier', fill: '#00ff00' });

        this.container = this.add.container(200, 300, [ image, text ]);
    }

    update ()
    {
        this.container.rotation += 0.01;
    }
}

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

const game = new Phaser.Game(config);

Зачем нужны Container?

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

Основные преимущества: * **Единое управление трансформациями:** Вы можете применять перемещение (`x,y), вращение (rotation) и масштабирование (scaleX,scaleY`) ко всему контейнеру, и эти изменения автоматически применятся ко всем его детям относительно его точки позиционирования. * **Локальные координаты:** Дочерние объекты добавляются в контейнер с локальными координатами (относительно его центра). Это упрощает компоновку сложных объектов. * **Иерархия сцены:** Контейнер сам является игровым объектом (GameObject), что позволяет легко добавлять, удалять или манипулировать всей группой через методы сцены.

Создание сцены и загрузка ассетов

В методе preload() мы загружаем единственный спрайт — изображение лемминга. Обратите внимание на использование this.load.setBaseURL(), которое задаёт базовый URL для всех последующих загрузок, что делает пути к ресурсам короче и удобнее.

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

Создание объектов и их группировка в Container

В методе create() происходит основная магия. Сначала мы создаём два независимых игровых объекта: изображение (Image) и текстовую метку (Text). Важный нюанс — их начальные координаты (0, 0) и (60, 0) задаются не относительно экрана, а относительно будущего центра контейнера.

Затем с помощью this.add.container() мы создаём контейнер, передавая ему глобальные координаты на сцене (200, 300) и массив дочерних объектов. Контейнер становится родителем для изображения и текста.

create ()
{
    //  Создаём два отдельных игровых объекта с локальными координатами
    const image = this.add.image(0, 0, 'lemming');
    const text = this.add.text(60, 0, 'Oh No!', { font: '16px Courier', fill: '#00ff00' });

    //  Группируем их в контейнер, задавая его глобальную позицию
    this.container = this.add.container(200, 300, [ image, text ]);
}

Анимация вращения контейнера

Метод update() вызывается на каждом кадре игры. Здесь мы изменяем свойство rotation нашего контейнера. Поскольку оба дочерних объекта (изображение и текст) привязаны к контейнеру, они вращаются вместе с ним вокруг его точки позиционирования (200, 300), сохраняя свои локальные позиции относительно друг друга. Это избавляет нас от необходимости вручную рассчитывать новые координаты для каждого объекта по тригонометрическим формулам.

update ()
{
    this.container.rotation += 0.01;
}

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

Это стандартная конфигурация игры Phaser. Ключевой момент — в поле scene передаётся класс нашей сцены Example. Это регистрирует сцену в игре. Остальные параметры задают размеры холста, цвет фона и DOM-элемент, в который будет встроена игра.

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

const game = new Phaser.Game(config);

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

Контейнеры — это фундаментальный инструмент для организации кода и создания сложных игровых сущностей в Phaser 3. Они позволяют мыслить абстракциями более высокого уровня, что делает код чище и управляемее. **Идеи для экспериментов:** 1. Добавьте в контейнер больше объектов разных типов (например, графику this.add.graphics()). 2. Поэкспериментируйте с другими свойствами контейнера: попробуйте изменить container.scaleX или container.scaleY в update(). 3. Сделайте так, чтобы контейнер двигался по сцене (меняйте container.x), и наблюдайте, как вся группа объектов следует за ним. 4. Создайте контейнер внутри другого контейнера для построения сложных иерархических объектов.