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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    graphics;
    path;
    follower;

    create ()
    {
        this.graphics = this.add.graphics();

        this.follower = { t: 0, vec: new Phaser.Math.Vector2() };

        this.path = new Phaser.Curves.Path(400, 300);

        this.path.circleTo(100);

        this.path.moveTo(400, 300);

        //  Rotate this circle so it completes the loop
        this.path.circleTo(100, true, 180);

        this.tweens.add({
            targets: this.follower,
            t: 1,
            ease: 'Linear',
            duration: 2000,
            repeat: -1
        });
    }

    update ()
    {
        this.graphics.clear();
        this.graphics.lineStyle(2, 0xffffff, 1);

        this.path.draw(this.graphics);

        this.path.getPoint(this.follower.t, this.follower.vec);

        this.graphics.fillStyle(0xff0000, 1);
        this.graphics.fillCircle(this.follower.vec.x, this.follower.vec.y, 12);
    }
}

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

const game = new Phaser.Game(config);

Инициализация сцены и создание пути

В методе create() сцены происходит базовая настройка. Сначала мы создаем объект Graphics для визуального отображения пути на экране.

Затем определяется объект-последователь (follower). Это простой объект с двумя свойствами: `t(текущая позиция на кривой от 0 до 1) иvec` (вектор для хранения расчетных координат).

Далее создается сам путь (Path). Конструктор принимает начальные координаты.

this.graphics = this.add.graphics();
this.follower = { t: 0, vec: new Phaser.Math.Vector2() };
this.path = new Phaser.Curves.Path(400, 300);

Построение фигуры из двух кругов

Ключевая часть — построение сложной фигуры. Метод circleTo() объекта Path добавляет к пути эллиптическую кривую. Первый вызов рисует круг радиуса 100 пикселей, начиная от текущей точки пути (400, 300).

Затем moveTo() возвращает «перо» в исходную точку, не рисуя соединительную линию. Второй вызов circleTo() с параметром true (рисовать по часовой стрелке) и начальным углом 180 градусов создает второй круг, который начинается с противоположной стороны от первого, формируя фигуру в виде «восьмерки» или двойной петли.

this.path.circleTo(100);
this.path.moveTo(400, 300);
this.path.circleTo(100, true, 180);

Анимация движения по пути

Чтобы объект двигался по пути, используется система твинов Phaser. Мы создаем твин, который бесконечно (repeat: -1) и линейно (ease: 'Linear') изменяет свойство `tобъекта-последователя от 0 до 1 за 2 секунды (duration: 2000). Именно изменение значенияt` от начала (0) до конца (1) пути обеспечивает перемещение.

this.tweens.add({
    targets: this.follower,
    t: 1,
    ease: 'Linear',
    duration: 2000,
    repeat: -1
});

Визуализация пути и объекта в реальном времени

В методе update() происходит отрисовка кадра. Сначала graphics.clear() стирает предыдущий кадр. Затем задается стиль линии и рисуется сам путь с помощью path.draw().

Сердцевина логики движения — вызов path.getPoint(this.follower.t, this.follower.vec). Этот метод вычисляет координаты (x, y) на кривой для текущего значения `tи записывает их в векторthis.follower.vec`.

Наконец, в этих вычисленных координатах рисуется красный круг, представляющий движущийся объект.

this.graphics.clear();
this.graphics.lineStyle(2, 0xffffff, 1);
this.path.draw(this.graphics);

this.path.getPoint(this.follower.t, this.follower.vec);

this.graphics.fillStyle(0xff0000, 1);
this.graphics.fillCircle(this.follower.vec.x, this.follower.vec.y, 12);

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

Объект Phaser.Curves.Path предоставляет мощный и гибкий инструмент для описания предопределенных траекторий. Комбинируя простые кривые, такие как circleTo(), ellipseTo() или splineTo(), и управляя «пером» с помощью moveTo(), можно создавать траектории любой сложности. Для экспериментов попробуйте: изменить радиус кругов, добавить третий сегмент пути, привязать к движению по пути не точку, а спрайт с физическим телом или изменить ease твина на 'Sine.easeInOut', чтобы скорость объекта менялась плавно.