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

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

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

Живой запуск

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

Исходный код


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

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

        this.bounds = new Phaser.Geom.Rectangle();

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

        //  The curves do not have to be joined
        const line1 = new Phaser.Curves.Line([ 100, 100, 500, 200 ]);
        const line2 = new Phaser.Curves.Line([ 200, 300, 600, 500 ]);

        this.path = this.add.path();

        this.path.add(line1);
        this.path.add(line2);

        this.path.getBounds(this.bounds);

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

    update ()
    {
        this.graphics.clear();

        //  Draw the bounds
        this.graphics.lineStyle(1, 0x00ff00, 1).strokeRectShape(this.bounds);

        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.fillRect(this.follower.vec.x - 8, this.follower.vec.y - 8, 16, 16);
    }
}

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

const game = new Phaser.Game(config);

Создание составного пути

Вместо одной сложной кривой часто удобнее создать путь из нескольких простых сегментов. В примере используются два прямых отрезка (Phaser.Curves.Line), которые не обязательно должны быть соединены друг с другом.

const line1 = new Phaser.Curves.Line([100, 100, 500, 200]);
const line2 = new Phaser.Curves.Line([200, 300, 600, 500]);

this.path = this.add.path();
this.path.add(line1);
this.path.add(line2);

Объект this.path типа Phaser.Curves.Path становится контейнером для этих кривых. Порядок добавления (add) определяет последовательность движения по ним. Это мощный подход для построения ломаных траекторий.

Вычисление границ (Bounds) пути

Чтобы получить прямоугольную область, охватывающую весь путь (например, для проверки видимости или столкновений с областью), используется метод getBounds. Он принимает пустой объект Phaser.Geom.Rectangle и заполняет его координатами.

this.bounds = new Phaser.Geom.Rectangle();
this.path.getBounds(this.bounds);

В методе update эти границы отрисовываются зелёным контуром с помощью graphics.strokeRectShape. Это наглядно показывает рассчитанную зону, которая учитывает все точки всех кривых в пути.

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

Для плавного перемещения объекта по пути используется твин, управляющий значением `t(от 0 до 1) в объекте-последователе (follower`). Это значение представляет прогресс движения по всему составному пути.

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

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

Твин непрерывно меняет `tот начала до конца пути и обратно (yoyo: true`), создавая циклическое движение.

Получение позиции и отрисовка

Каждый кадр в update мы берём текущее значение `tизfollowerи получаем соответствующую ей точку в мировых координатах. МетодgetPointзаписывает результат в переданный векторvec`.

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

Затем эта позиция используется для отрисовки красного квадрата, представляющего движущийся объект. Сам путь также отрисовывается белыми линиями через path.draw(graphics).

this.graphics.fillRect(this.follower.vec.x - 8, this.follower.vec.y - 8, 16, 16);

Важно очищать графику (graphics.clear()) каждый кадр, чтобы избежать наложения.

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

Использование Path как контейнера для кривых, расчёт его границ и анимация через твин — это основа для создания управляемых траекторий. Для экспериментов попробуйте заменить Line на Spline или Ellipse, изменить параметры твина (например, ease) для нелинейного движения или использовать полученные границы (bounds) для активации событий при входе/выходе объекта из зоны.