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

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

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

    create ()
    {
        const image1 = this.add.image(0, -30, 'mushroom');
        const image2 = this.add.image(-40, 30, 'mushroom');
        const image3 = this.add.image(40, 30, 'mushroom');

        this.container = this.add.container(400, 200, [ image1, image2, image3 ]);

        //  A Container has a default size of 0x0, so we need to give it a size before enabling a physics
        //  body or it'll be given the default body size of 64x64.
        this.container.setSize(128, 64);

        this.physics.world.enable(this.container);

        this.container.body.setVelocity(100, 200).setBounce(1, 1).setCollideWorldBounds(true);
    }

    update ()
    {
        if (this.container.body.velocity.x < 0)
        {
            this.container.rotation -= 0.02;
        }
        else
        {
            this.container.rotation += 0.02;
        }
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#010101',
    parent: 'phaser-example',
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: 200 },
            debug: true
        }
    },
    scene: Example
};

const game = new Phaser.Game(config);

Что такое Container и зачем ему физика?

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

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

Ключевой шаг: задание размера контейнера

Самая важная и часто упускаемая деталь — перед включением физики для контейнера необходимо явно задать его размер. Без этого тело получит размер по умолчанию (64x64 пикселя), что может не соответствовать реальным границам вашей группы спрайтов.

В нашем примере три гриба расположены так, что общая занимаемая область примерно 128x64 пикселя. Мы явно указываем этот размер.

//  Контейнер имеет размер 0x0 по умолчанию.
//  Мы задаем размер вручную перед включением физики.
this.container.setSize(128, 64);

Только после этого мы активируем физическое тело для контейнера с помощью this.physics.world.enable().

Создание и настройка физического тела

После задания размера мы включаем физику Arcade для объекта контейнера. Метод enable создает и присоединяет к контейнеру тело типа Arcade Physics Body.

Затем мы настраиваем это тело, как и любое другое: задаем начальную скорость, упругость (отскок) и включаем столкновение с границами игрового мира.

// Включаем физику Arcade для контейнера
this.physics.world.enable(this.container);

// Настраиваем свойства тела контейнера
this.container.body.setVelocity(100, 200).setBounce(1, 1).setCollideWorldBounds(true);

Теперь контейнер, как единый объект, будет летать по сцене, отскакивать от стен (setBounce(1,1) означает 100% упругость по обеим осям) и оставаться в пределах холста.

Связываем физику с визуалом: вращение в update()

Физическое тело управляет положением и скоростью контейнера. Мы можем использовать эти данные, чтобы влиять на его визуальные свойства. В методе update(), который вызывается каждый кадр, мы проверяем горизонтальную скорость тела.

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

update ()
{
    if (this.container.body.velocity.x < 0)
    {
        // Если движемся влево, вращаем против часовой стрелки
        this.container.rotation -= 0.02;
    }
    else
    {
        // Если движемся вправо или стоит, вращаем по часовой стрелке
        this.container.rotation += 0.02;
    }
}

Обратите внимание: мы меняем свойство rotation самого контейнера (this.container), а не его физического тела.

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

Использование Container в связке с Arcade Physics позволяет легко создавать сложные составные объекты, которые ведут себя в мире как единое целое. Главное правило — не забывать задавать корректный размер контейнера с помощью setSize() перед включением физики. Для экспериментов попробуйте: 1. Добавить в контейнер больше спрайтов разных размеров и подобрать новый размер для setSize(). 2. Заставить контейнер реагировать на клики или ввод с клавиатуры, применяя силу (setAcceleration) или импульс (setVelocity) к this.container.body. 3. Реализовать столкновение контейнера с другими физическими телами на сцене с помощью this.physics.add.collider().