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

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

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

Живой запуск

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

Исходный код


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

        this.i = 0;
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.spritesheet('balls', 'assets/sprites/balls.png', { frameWidth: 17, frameHeight: 17 });
    }

    create ()
    {
        this.rect = new Phaser.Geom.Rectangle(64, 32, 100, 512);

        this.group = this.add.group({ key: 'balls', frame: [0,1,2,3,4,5], frameQuantity: 10 });

        this.tweens.add({
            targets: this.rect,
            x: 200,
            y: 200,
            width: 512,
            height: 100,
            delay: 2000,
            duration: 3000,
            ease: 'Sine.easeInOut',
            repeat: -1,
            yoyo: true
        });
    }

    update ()
    {
        Phaser.Actions.PlaceOnRectangle(this.group.getChildren(), this.rect, this.i);

        this.i++;

        if (this.i === this.group.length)
        {
            this.i = 0;
        }
    }
}

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

const game = new Phaser.Game(config);

Подготовка сцены и данных

В примере создаётся простая сцена. В preload загружается спрайтшит с шариками. Ключевые объекты создаются в методе create.

Во-первых, определяется прямоугольник this.rect, который будет служить траекторией для размещения объектов. Его начальные координаты и размеры задаются конструктором.

this.rect = new Phaser.Geom.Rectangle(64, 32, 100, 512);

Во-вторых, создаётся группа спрайтов this.group. Группа автоматически создаёт 10 спрайтов (frameQuantity) для каждого из шести кадров (frame) из спрайтшита 'balls'.

this.group = this.add.group({ key: 'balls', frame: [0,1,2,3,4,5], frameQuantity: 10 });

Также в constructor инициализируется счётчик this.i, который будет использоваться для смещения.

constructor ()
{
    super();
    this.i = 0;
}

Анимация прямоугольника-траектории

Чтобы визуализация была наглядной, прямоугольник не статичен. На него навешивается твин, который циклично меняет его положение и размеры.

this.tweens.add({
    targets: this.rect,
    x: 200,
    y: 200,
    width: 512,
    height: 100,
    delay: 2000,
    duration: 3000,
    ease: 'Sine.easeInOut',
    repeat: -1,
    yoyo: true
});

Этот код анимирует свойства `x,y,widthиheightобъектаthis.rect`. Твин стартует с задержкой в 2 секунды, длится 3 секунды, использует плавную эasing-функцию и повторяется бесконечно с эффектом 'йо-йо' (движение вперёд и назад). Таким образом, форма и положение траектории постоянно меняются.

Динамическое размещение объектов в update

Сердце примера — метод update, который вызывается на каждом кадре. Здесь происходит магия размещения.

Phaser.Actions.PlaceOnRectangle(this.group.getChildren(), this.rect, this.i);

Метод PlaceOnRectangle принимает три аргумента: 1. Массив объектов для размещения (все дети группы, полученные через getChildren()). 2. Прямоугольник (Phaser.Geom.Rectangle), по периметру которого будут расположены объекты. 3. Смещение (this.i). Это целочисленное значение, определяющее, с какой позиции на периметре прямоугольника начнётся расстановка.

После каждого вызова индекс смещения увеличивается, а когда достигает количества объектов в группе — сбрасывается в ноль. Это создаёт эффект циклического сдвига всех объектов по траектории.

this.i++;
if (this.i === this.group.length)
{
    this.i = 0;
}

Поскольку update выполняется постоянно, а прямоугольник анимирован твином, спрайты плавно 'едут' по меняющейся замкнутой фигуре.

Инициализация игры (конфигурация)

Запуск игры происходит стандартным для Phaser 3 образом. Конфигурационный объект задаёт тип рендерера, размеры холста, родительский HTML-элемент и нашу сцену Example.

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

const game = new Phaser.Game(config);

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

Phaser.Actions.PlaceOnRectangle — это простой, но мощный инструмент для расположения объектов по контуру. В связке с твинами и логикой обновления он позволяет создавать сложные динамические паттерны буквально в несколько строк кода. **Идеи для экспериментов:** 1. Замените прямоугольник на круг (Phaser.Geom.Circle) и используйте PlaceOnCircle. 2. Изменяйте смещение this.i не на 1, а на другое число, или привяжите его к времени для контроля скорости 'прокрутки'. 3. Используйте разные easing-функции для твина прямоугольника, чтобы получить более резкие или причудливые трансформации траектории. 4. Добавьте физические тела к объектам в группе и посмотрите, как они будут взаимодействовать, двигаясь по заданному пути.