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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

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

    create ()
    {
        this.add.text(10, 10, 'Click to add Sprite to Container').setDepth(1);

        const size = this.add.text(10, 32, 'Container size: 0').setDepth(1);

        const container = this.add.container();

        this.input.on('pointerdown', pointer => {

            const x = pointer.worldX;
            const y = pointer.worldY;

            const sprite = new Phaser.GameObjects.Sprite(this, x, y, 'cake');

            container.add(sprite);

            size.setText('Container size: ' + container.length);

        });
    }
}

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

const game = new Phaser.Game(config);

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

Основой любого примера в Phaser является класс сцены, расширяющий Phaser.Scene. В его методе preload мы загружаем графические ресурсы, которые будут использоваться в качестве спрайтов.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('beer', 'assets/sprites/beer.png');
    // ... другие изображения
}

Метод setBaseURL устанавливает базовый URL для загрузчиков, что позволяет указывать только относительные пути к файлам. Загружаемые изображения (например, 'cake') позже будут использоваться как текстуры для спрайтов.

Инициализация контейнера и текстовых полей

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

create ()
{
    this.add.text(10, 10, 'Click to add Sprite to Container').setDepth(1);
    const size = this.add.text(10, 32, 'Container size: 0').setDepth(1);
    const container = this.add.container();
}

Метод this.add.container() создает новый пустой контейнер и автоматически добавляет его на сцену. Контейнер — это особый тип игрового объекта, который может содержать в себе других детей (дочерние объекты), включая другие контейнеры. Установка setDepth(1) для текста гарантирует, что он будет отображаться поверх других графических элементов.

Обработка клика и динамическое создание спрайтов

Ключевая логика примера привязана к событию клика мыши. Мы настраиваем слушатель на событие 'pointerdown'.

this.input.on('pointerdown', pointer => {
    const x = pointer.worldX;
    const y = pointer.worldY;
    const sprite = new Phaser.GameObjects.Sprite(this, x, y, 'cake');
});

Свойства pointer.worldX и pointer.worldY содержат координаты клика в игровом мире. На их основе создается новый экземпляр спрайта Phaser.GameObjects.Sprite. Конструктор принимает ссылку на сцену (this), координаты и ключ текстуры, загруженной ранее.

Добавление спрайта в контейнер и обновление счетчика

Созданный спрайт необходимо добавить в контейнер. Это делается с помощью метода add контейнера.

container.add(sprite);
size.setText('Container size: ' + container.length);

Метод container.add(sprite) помещает спрайт в контейнер. С этого момента спрайт становится дочерним элементом контейнера. Его позиция, вращение и масштаб будут рассчитываться относительно контейнера, если у контейнера будут заданы соответствующие трансформации. Свойство container.length отражает текущее количество дочерних объектов в контейнере, и мы обновляем текстовое поле, чтобы отобразить это число.

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

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

  1. добавлять спрайты с разными текстурами ('beer', 'watermelon') в зависимости от позиции клика
  2. применить трансформации (например, container.setRotation(0.5)) ко всему контейнеру после добавления нескольких спрайтов и наблюдать, как все дети меняются вместе
  3. реализовать удаление спрайтов из контейнера по второму клику, используя container.remove(sprite) и обновляя счетчик