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

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

Версия 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.script('webfont', '//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js');
        this.load.image('ball', 'assets/demoscene/doc-ball.png');
    }

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

        const ball = this.add.image(0, 0, 'ball');
        const text = this.add.text(0, 0, 'Testing');
        text.font = 'Arial';
        text.setOrigin(0.5, 0.5);

        container.add(ball);
        container.add(text);
        container.setScale(4);

        this.tweens.add({
            targets: container,
            x: container.x + 100,
            ease: 'Power1',
            duration: 5000,
            delay: 500,
            yoyo: true,
            repeat: -1
        });
    }
}

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

const game = new Phaser.Game(config);

Загрузка ресурсов и инициализация

Подготовка сцены начинается с метода preload. В нем мы загружаем изображение шара и подключаем сторонний скрипт WebFont (хотя в данном примере он не используется напрямую, он демонстрирует возможность загрузки скриптов). Важно указать базовый URL для загрузки ресурсов, чтобы использовать относительные пути.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.script('webfont', '//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js');
    this.load.image('ball', 'assets/demoscene/doc-ball.png');
}

Создание контейнера и добавление объектов

В методе create мы создаем контейнер с центром в точке (400, 300) на сцене. Контейнер сам по себе не имеет графического представления, он служит родительским преобразованием для своих дочерних элементов.

Затем создаются два игровых объекта: изображение шара (ball) и текстовое поле (text). Обратите внимание, что их начальные координаты (0, 0) задаются относительно позиции контейнера. Для текста мы устанавливаем шрифт и меняем его точку привязки (origin) на центр, чтобы он корректно масштабировался и вращался вместе с контейнером.

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

const ball = this.add.image(0, 0, 'ball');
const text = this.add.text(0, 0, 'Testing');
text.font = 'Arial';
text.setOrigin(0.5, 0.5);

container.add(ball);
container.add(text);

Трансформация контейнера

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

container.setScale(4);

Анимация контейнера с помощью Tween

Сила контейнеров раскрывается при анимации. Вместо того чтобы создавать отдельные твины для шара и текста, мы анимируем сам контейнер. Твин перемещает контейнер на 100 пикселей вправо по оси X, используя функцию плавности Power1. Анимация длится 5 секунд, начинается с задержкой в 0.5 секунды, а параметры yoyo и repeat заставляют ее бесконечно колебаться туда-обратно.

Поскольку контейнер — это цель анимации (targets), оба его дочерних объекта плавно движутся вместе, сохраняя свою внутреннюю структуру.

this.tweens.add({
    targets: container,
    x: container.x + 100,
    ease: 'Power1',
    duration: 5000,
    delay: 500,
    yoyo: true,
    repeat: -1
});

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

Это стандартная конфигурация для создания экземпляра игры Phaser. Мы используем WebGL-рендерер, устанавливаем размеры холста, цвет фона и указываем, какая сцена (scene) будет использоваться. Ключ parent определяет ID HTML-элемента, в который будет встроен canvas.

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

const game = new Phaser.Game(config);

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

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