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

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

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

    create ()
    {
        this.group1 = this.add.group({ key: 'ball', frameQuantity: 16 });
        this.group2 = this.add.group({ key: 'ball', frameQuantity: 16 });
        this.group3 = this.add.group({ key: 'ball', frameQuantity: 16 });
        this.group4 = this.add.group({ key: 'ball', frameQuantity: 16 });

        Phaser.Actions.PlaceOnCircle(this.group1.getChildren(), { x: 400, y: 300, radius: 200 });
        Phaser.Actions.PlaceOnCircle(this.group2.getChildren(), { x: 400, y: 300, radius: 160 });
        Phaser.Actions.PlaceOnCircle(this.group3.getChildren(), { x: 400, y: 300, radius: 120 });
        Phaser.Actions.PlaceOnCircle(this.group4.getChildren(), { x: 400, y: 300, radius: 80 });
    }

    update ()
    {
        Phaser.Actions.RotateAroundDistance(this.group1.getChildren(), { x: 400, y: 300 }, 0.02, 200);
        Phaser.Actions.RotateAroundDistance(this.group2.getChildren(), { x: 400, y: 300 }, 0.02, 160);
        Phaser.Actions.RotateAroundDistance(this.group3.getChildren(), { x: 400, y: 300 }, 0.02, 120);
        Phaser.Actions.RotateAroundDistance(this.group4.getChildren(), { x: 400, y: 300 }, 0.02, 80);
    }
}

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

const game = new Phaser.Game(config);

Подготовка сцены и создание групп спрайтов

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

Ключевой этап происходит в create. Здесь создаются четыре группы спрайтов (Group) с помощью метода this.add.group. Каждая группа сразу заполняется 16-ю экземплярами спрайта 'ball' (параметр frameQuantity). Группы в Phaser — это удобный способ управления коллекциями игровых объектов.

this.group1 = this.add.group({ key: 'ball', frameQuantity: 16 });
this.group2 = this.add.group({ key: 'ball', frameQuantity: 16 });
this.group3 = this.add.group({ key: 'ball', frameQuantity: 16 });
this.group4 = this.add.group({ key: 'ball', frameQuantity: 16 });

Размещение объектов по окружности: PlaceOnCircle

После создания групп все спрайты находятся в одной точке. Чтобы распределить их, используется статический метод Phaser.Actions.PlaceOnCircle. Он принимает массив объектов (в нашем случае — результат вызова group.getChildren()) и объект конфигурации, описывающий окружность.

Конфигурация включает центр окружности (`x,y) и ееradius`. Метод равномерно распределяет все переданные объекты по полной окружности (360 градусов). В примере мы создаем четыре концентрические окружности с разными радиусами.

Phaser.Actions.PlaceOnCircle(this.group1.getChildren(), { x: 400, y: 300, radius: 200 });
Phaser.Actions.PlaceOnCircle(this.group2.getChildren(), { x: 400, y: 300, radius: 160 });
Phaser.Actions.PlaceOnCircle(this.group3.getChildren(), { x: 400, y: 300, radius: 120 });
Phaser.Actions.PlaceOnCircle(this.group4.getChildren(), { x: 400, y: 300, radius: 80 });

Анимация вращения: RotateAroundDistance

Статическое расположение — это только начало. Чтобы оживить композицию, в методе update (вызываемом каждый кадр) применяется Phaser.Actions.RotateAroundDistance. Этот метод вращает массив объектов вокруг заданной точки.

Он принимает массив объектов, точку вращения (объект с `xиy), угол поворота в радианах за вызов и исходное расстояние от объектов до центра (радиус). Важно передавать тот жеradius`, что и при размещении, чтобы объекты оставались на своей орбите и не "спиралились" к центру или от него. Положительное значение угла (0.02) задает вращение против часовой стрелки.

Phaser.Actions.RotateAroundDistance(this.group1.getChildren(), { x: 400, y: 300 }, 0.02, 200);
Phaser.Actions.RotateAroundDistance(this.group2.getChildren(), { x: 400, y: 300 }, 0.02, 160);
// ... и так далее для каждой группы

Настройка игры (Config) и запуск

Работа Phaser-приложения начинается с конфигурационного объекта, который передается в конструктор Phaser.Game. В этом примере используется базовая конфигурация:

- type: Phaser.AUTO позволяет Phaser самому выбрать рендерер (WebGL или Canvas). - width и height: устанавливают размер игрового поля. - backgroundColor: цвет фона в HEX-формате. - parent: ID HTML-элемента, в который будет встроен canvas. - scene: класс главной сцены, которая будет запущена сразу.

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

const game = new Phaser.Game(config);

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

Методы PlaceOnCircle и RotateAroundDistance из модуля Actions — это мощный и лаконичный инструмент для создания сложных круговых анимаций без необходимости писать тригонометрические расчеты вручную. Для экспериментов попробуйте изменить количество спрайтов в группах, угловую скорость вращения (например, сделать внутренние круги быстрее внешних) или динамически менять радиус в update, чтобы создать эффект "пульсации". Также можно комбинировать эти действия с другими, например SetAlpha или SetTint, для создания более ярких визуальных эффектов.