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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    controls;

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

    create ()
    {
        this.cameras.main.zoom = 0.5;

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

        //  Add some sprites - positions are relative to the Container x/y
        const sprite0 = this.add.sprite(0, 0, 'lemming');
        const sprite1 = this.add.sprite(-100, -100, 'lemming');
        const sprite2 = this.add.sprite(100, -100, 'lemming');
        const sprite3 = this.add.sprite(100, 100, 'lemming');
        const sprite4 = this.add.sprite(-100, 100, 'lemming');

        container.add(sprite0);
        container.add(sprite1);
        container.add(sprite2);
        container.add(sprite3);
        container.add(sprite4);

        //  You could also pass them in as an array, to save doing them one by one

        this.tweens.add({
            targets: container,
            angle: 360,
            duration: 6000,
            yoyo: true,
            repeat: -1
        });

        const cursors = this.input.keyboard.createCursorKeys();

        const controlConfig = {
            camera: this.cameras.main,
            left: cursors.left,
            right: cursors.right,
            up: cursors.up,
            down: cursors.down,
            zoomIn: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q),
            zoomOut: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E),
            acceleration: 0.06,
            drag: 0.0005,
            maxSpeed: 1.0
        };

        this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);

        const cam = this.cameras.main;

        const gui = new dat.GUI();

        const help = {
            line1: 'Cursors to move',
            line2: 'Q & E to zoom'
        };

        const f1 = gui.addFolder('Camera');
        f1.add(cam, 'x').listen();
        f1.add(cam, 'y').listen();
        f1.add(cam, 'scrollX').listen();
        f1.add(cam, 'scrollY').listen();
        f1.add(cam, 'rotation').min(0).step(0.01).listen();
        f1.add(cam, 'zoom', 0.1, 2).step(0.1).listen();
        f1.add(help, 'line1');
        f1.add(help, 'line2');
        f1.open();
    }

    update (time, delta)
    {
        this.controls.update(delta);
    }
}

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

const game = new Phaser.Game(config);

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

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

В методе create() мы создаем контейнер в центре сцены. Затем создаем пять спрайтов лемминга. Обратите внимание: координаты спрайтов задаются относительно позиции контейнера. Например, спрайт с координатами (-100, -100) будет расположен на 100 пикселей левее и выше центра контейнера.

const container = this.add.container(400, 300);
const sprite0 = this.add.sprite(0, 0, 'lemming');
const sprite1 = this.add.sprite(-100, -100, 'lemming');
const sprite2 = this.add.sprite(100, -100, 'lemming');
const sprite3 = this.add.sprite(100, 100, 'lemming');
const sprite4 = this.add.sprite(-100, 100, 'lemming');
container.add(sprite0);
container.add(sprite1);
container.add(sprite2);
container.add(sprite3);
container.add(sprite4);

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

Чтобы оживить сцену, мы применяем анимацию (Tween) к самому контейнеру. Анимация вращает контейнер на 360 градусов, затем возвращает его обратно и повторяет бесконечно. Поскольку спрайты являются дочерними объектами, они вращаются вместе с контейнером, сохраняя свои относительные позиции.

Ключевые параметры твина: - targets: объект для анимации (наш контейнер). - angle: целевой угол вращения. - duration: длительность анимации в миллисекундах. - yoyo: если true, анимация проигрывается в обратном порядке после завершения. - repeat: количество повторений (-1 для бесконечного повторения).

this.tweens.add({
    targets: container,
    angle: 360,
    duration: 6000,
    yoyo: true,
    repeat: -1
});

Плавное управление камерой

Для интерактивного управления камерой в примере используется Phaser.Cameras.Controls.SmoothedKeyControl. Этот контроллер обеспечивает плавное движение и зум камеры с инерцией, что делает управление более приятным.

Конфигурация контроллера связывает клавиши стрелок для перемещения и клавиши Q/E для зума. Параметры acceleration, drag и maxSpeed настраивают физику движения: ускорение, замедление и максимальную скорость.

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

const cursors = this.input.keyboard.createCursorKeys();
const controlConfig = {
    camera: this.cameras.main,
    left: cursors.left,
    right: cursors.right,
    up: cursors.up,
    down: cursors.down,
    zoomIn: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q),
    zoomOut: this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E),
    acceleration: 0.06,
    drag: 0.0005,
    maxSpeed: 1.0
};
this.controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
update (time, delta)
{
    this.controls.update(delta);
}

Настройка камеры и отладка с dat.GUI

В примере камера сразу уменьшает масштаб в два раза (zoom = 0.5), чтобы мы могли видеть больше игрового пространства. Для отладки и наглядности используется библиотека dat.GUI, которая создает панель управления в реальном времени.

На панели отображаются и изменяются ключевые свойства камеры: её мировые координаты (`x,y), смещение (scrollX,scrollY), вращение и зум. Метод.listen()` заставляет значения автоматически обновляться в интерфейсе при их изменении в коде.

this.cameras.main.zoom = 0.5;
const cam = this.cameras.main;
const gui = new dat.GUI();
const f1 = gui.addFolder('Camera');
f1.add(cam, 'x').listen();
f1.add(cam, 'y').listen();
f1.add(cam, 'scrollX').listen();
f1.add(cam, 'scrollY').listen();
f1.add(cam, 'rotation').min(0).step(0.01).listen();
f1.add(cam, 'zoom', 0.1, 2).step(0.1).listen();

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

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