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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();

        this.gingerbreads = [];
    }

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

    create ()
    {
        for (let i = 0; i < 26; i++)
        {
            this.gingerbreads.push(this.add.image(i * 32, 300, 'gingerbread'));
        }
    }

    update ()
    {
        Phaser.Actions.Angle(this.gingerbreads, 1.5, 0.1);
    }
}

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

const game = new Phaser.Game(config);

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

Класс сцены наследуется от Phaser.Scene. В конструкторе инициализируется пустой массив gingerbreads, который будет хранить ссылки на все создаваемые спрайты.

class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
        this.gingerbreads = [];
    }

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

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

Создание ряда спрайтов

В методе create в цикле создается 26 спрайтов. Каждый новый спрайт добавляется на сцену с помощью this.add.image и помещается в массив gingerbreads. Координата X каждого следующего спрайта увеличивается на 32 пикселя, формируя горизонтальный ряд.

create ()
    {
        for (let i = 0; i < 26; i++)
        {
            this.gingerbreads.push(this.add.image(i * 32, 300, 'gingerbread'));
        }
    }

Массовое вращение через Phaser.Actions

Сердце примера — метод update, который вызывается на каждом кадре игры. Здесь используется статический метод Phaser.Actions.Angle. Он применяет изменение угла ко всем объектам в переданном массиве.

update ()
    {
        Phaser.Actions.Angle(this.gingerbreads, 1.5, 0.1);
    }

Разберем аргументы функции: 1. this.gingerbreads — массив объектов (в нашем случае спрайтов), к которым нужно применить действие. 2. 1.5 — величина (step), на которую будет изменяться угол каждого объекта за вызов. Положительное значение означает вращение по часовой стрелке. 3. 0.1 — шаг (rate) для вычисления конечного значения. Фактически, итоговое изменение угла за кадр рассчитывается как step * rate. В нашем случае: 1.5 * 0.1 = 0.15 градуса за кадр. Этот механизм позволяет тонко настраивать скорость анимации.

Конфигурация и запуск игры

Стандартная конфигурация игры определяет ее тип, размеры холста, цвет фона и корневой сцены.

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

const game = new Phaser.Game(config);

После создания экземпляра Phaser.Game с этой конфигурацией автоматически запускается жизненный цикл сцены: preload, create, а затем циклически update.

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

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