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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    group;
    sprite;

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

    create ()
    {
        this.sprite = this.physics.add.image(400, 300, 'mushroom');

        this.group = this.physics.add.staticGroup({
            key: 'ball',
            frameQuantity: 30
        });

        Phaser.Actions.PlaceOnRectangle(this.group.getChildren(), new Phaser.Geom.Rectangle(84, 84, 616, 416));

        //  We need to call this because placeOnRectangle has changed the coordinates of all the children
        //  If we don't call it, the static physics bodies won't be updated to reflect them
        this.group.refresh();

        this.sprite.setVelocity(100, 200).setBounce(1, 1).setCollideWorldBounds(true).setGravityY(200);
    }

    update ()
    {
        this.physics.world.collide(this.sprite, this.group);
    }
}

const config = {
    type: Phaser.WEBGL,
    width: 800,
    height: 600,
    parent: 'phaser-example',
    physics: {
        default: 'arcade',
        arcade: {
            debug: true
        }
    },
    scene: Example
};

const game = new Phaser.Game(config);

Инициализация сцены и загрузка ассетов

В методе preload мы загружаем два изображения: гриб (mushroom) для движущегося спрайта и блестящий шар (ball) для статичных объектов. Обратите внимание на использование setBaseURL — это удобно для загрузки ресурсов из удаленного репозитория, но в реальном проекте вы, скорее всего, будете использовать локальные пути.

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

Создание физических тел

В методе create происходит основная настройка физики. Сначала создается динамическое тело — спрайт гриба с помощью this.physics.add.image. Оно будет двигаться под действием физики.

this.sprite = this.physics.add.image(400, 300, 'mushroom');

Затем создается статичная группа. Ключевой метод — this.physics.add.staticGroup. В параметрах мы указываем ключ текстуры (key) и количество создаваемых спрайтов (frameQuantity). Все созданные тела сразу будут статичными — они не подвержены силам гравитации или импульсам, но участвуют в коллизиях.

this.group = this.physics.add.staticGroup({
    key: 'ball',
    frameQuantity: 30
});

Расположение объектов и обновление физики

Созданные шары нужно расположить по фигуре. Для этого используется хелпер Phaser.Actions.PlaceOnRectangle. Он принимает массив детей группы (полученный через this.group.getChildren()) и объект прямоугольника Phaser.Geom.Rectangle. Дети будут равномерно распределены по периметру этой фигуры.

Phaser.Actions.PlaceOnRectangle(this.group.getChildren(), new Phaser.Geom.Rectangle(84, 84, 616, 416));

**Важный момент:** после изменения координат спрайтов через PlaceOnRectangle, их физические тела (статичные) не обновляются автоматически. Необходим вызов this.group.refresh(). Этот метод пересчитывает позиции и размеры всех физических тел в статичной группе, синхронизируя их с визуальными спрайтами.

this.group.refresh();

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

this.sprite.setVelocity(100, 200).setBounce(1, 1).setCollideWorldBounds(true).setGravityY(200);

Обработка столкновений и игровой цикл

В методе update, который вызывается каждый кадр, мы проверяем столкновение между движущимся спрайтом и всей статичной группой с помощью this.physics.world.collide. Phaser автоматически обрабатывает отскок, так как у спрайта установлено свойство setBounce(1, 1).

update ()
{
    this.physics.world.collide(this.sprite, this.group);
}

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

В конфигурации игры обязательно должен быть включен нужный физический плагин. В данном случае это arcade с опцией debug: true, которая отрисовывает хитбоксы — очень помогает при отладке.

const config = {
    type: Phaser.WEBGL,
    width: 800,
    height: 600,
    parent: 'phaser-example',
    physics: {
        default: 'arcade',
        arcade: {
            debug: true
        }
    },
    scene: Example
};

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

Использование статичной группы (staticGroup) — это оптимальный способ создания неподвижных препятствий в Phaser. Оно экономит ресурсы и упрощает код. Для экспериментов попробуйте

  1. Изменить форму расположения объектов с помощью PlaceOnCircle или PlaceOnEllipse
  2. Динамически добавлять и удалять тела из группы, не забывая вызывать refresh()
  3. Использовать разные текстуры для элементов группы, передав массив в параметр key