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

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

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

Живой запуск

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

Исходный код


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

        this.y = 0;
        this.gems = [];
    }

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

    create ()
    {
        //  This is our 'lead' Sprite, the first one in the array
        this.gems.push(this.add.sprite(200, 300, 'red'));

        for (let i = 0; i < 8; i++)
        {
            //  All of the blue gems will be aligned to the right of the red gem
            this.gems.push(this.add.sprite(0, 0, 'blue'));
        }
    }

    update ()
    {
        Phaser.Actions.AlignTo(this.gems, Phaser.Display.Align.RIGHT_CENTER, 0, Math.sin(this.y) * 8);

        this.y += 0.1;
    }
}

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

const game = new Phaser.Game(config);

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

В конструкторе класса сцены инициализируем два свойства: `yдля хранения текущего угла синусоиды и пустой массивgems` для хранения всех спрайтов, участвующих в выравнивании.

В методе preload загружаем два изображения. Обратите внимание на использование setBaseURL — это удобно, когда все ассеты лежат по одному базовому пути.

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

Создание ведущего и ведомых спрайтов

В методе create формируем нашу группу. Ключевой момент — первый спрайт, добавляемый в массив, становится "лидером" или точкой отсчёта для операции AlignTo. Его позиция задаётся явно.

Остальные спрайты (синие колонны) создаются с координатами (0, 0), так как их конечная позиция будет полностью переопределена в update методом AlignTo.

create ()
{
    //  This is our 'lead' Sprite, the first one in the array
    this.gems.push(this.add.sprite(200, 300, 'red'));

    for (let i = 0; i < 8; i++)
    {
        //  All of the blue gems will be aligned to the right of the red gem
        this.gems.push(this.add.sprite(0, 0, 'blue'));
    }
}

Магия динамического выравнивания в update

Вся анимация происходит в методе update, который вызывается на каждом кадре. Здесь используется статический метод Phaser.Actions.AlignTo.

Его первый аргумент — массив спрайтов. Второй — константа Phaser.Display.Align.RIGHT_CENTER. Она означает: "выровнять все спрайты по правому краю и центру по вертикали относительно предыдущего спрайта в массиве". Таким образом, первый синий спрайт выравнивается по правому краю красного, второй — по правому краю первого синего, и так далее.

Третий аргумент (смещение по X) равен 0. А вот четвёртый аргумент (смещение по Y) — это и есть источник динамики. Мы передаём результат вычисления Math.sin(this.y) * 8. Значение свойства this.y плавно увеличивается каждый кадр, заставляя синус колебаться между -1 и 1, что даёт итоговое смещение от -8 до 8 пикселей.

update ()
{
    Phaser.Actions.AlignTo(this.gems, Phaser.Display.Align.RIGHT_CENTER, 0, Math.sin(this.y) * 8);

    this.y += 0.1;
}

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

Это стандартная конфигурация игры Phaser. Обратите внимание, что в свойстве scene передаётся не строка с ключом, а сам класс нашей сцены Example. Это допустимый и удобный способ.

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

const game = new Phaser.Game(config);

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

Метод Phaser.Actions.AlignTo — это мощный инструмент для автоматического позиционирования групп объектов. Как мы увидели, его можно легко комбинировать с математическими функциями для создания живой, органической анимации без управления каждым объектом в отдельности. Для экспериментов попробуйте: 1. Изменить константу выравнивания на LEFT_CENTER или BOTTOM_CENTER. 2. Задать отличное от нуля смещение по X, чтобы цепочка шла по диагонали. 3. Использовать Math.cos или комбинацию синусов для более сложных траекторий движения всей цепочки. 4. Применить этот подход к выравниванию текстовых элементов или кнопок в меню, добавив им лёгкого "дыхания".