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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    graphics;
    points;
    curve;
    path;

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

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

        this.points = [];

        this.points.push(new Phaser.Math.Vector2(50, 400));
        this.points.push(new Phaser.Math.Vector2(200, 200));
        this.points.push(new Phaser.Math.Vector2(350, 300));
        this.points.push(new Phaser.Math.Vector2(500, 500));
        this.points.push(new Phaser.Math.Vector2(700, 400));

        this.curve = new Phaser.Curves.Spline(this.points);

        this.tweens.add({
            targets: this.path,
            t: 1,
            ease: 'Sine.easeInOut',
            duration: 2000,
            yoyo: true,
            repeat: -1
        });
    }

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

        this.graphics.lineStyle(1, 0xffffff, 1);

        this.curve.draw(this.graphics, 64);

        this.graphics.fillStyle(0x00ff00, 1);

        for (let i = 0; i < this.points.length; i++)
        {
            this.graphics.fillCircle(this.points[i].x, this.points[i].y, 4);
        }

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

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

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

const game = new Phaser.Game(config);

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

В методе create() происходит начальная настройка. Сначала создаётся объект Graphics для рисования. Затем инициализируется объект path, который будет хранить текущую позицию на кривой (параметр `t` от 0 до 1) и вектор для вычислений.

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

Далее создаётся массив опорных точек (points), через которые будет проходить кривая. Каждая точка — это вектор с координатами X и Y.

this.points = [];
this.points.push(new Phaser.Math.Vector2(50, 400));
this.points.push(new Phaser.Math.Vector2(200, 200));
// ... и так далее

На основе этого массива создаётся сам объект сплайна.

this.curve = new Phaser.Curves.Spline(this.points);

Анимация движения по кривой

Чтобы точка двигалась по сплайну, используется твин из системы Tweens. Он циклически меняет значение this.path.t от 0 до 1 и обратно, что соответствует движению от начала кривой к её концу и назад.

this.tweens.add({
    targets: this.path,
    t: 1,
    ease: 'Sine.easeInOut',
    duration: 2000,
    yoyo: true,
    repeat: -1
});

Ключевые параметры: - targets: объект, свойство которого будет анимировано. - `t`: целевое значение (1 — конец кривой). - ease: функция плавности 'Sine.easeInOut' для мягкого старта и остановки. - yoyo: true: заставляет твин проигрываться в обратном направлении. - repeat: -1: бесконечное повторение.

Визуализация в реальном времени

В методе update() происходит отрисовка кадра. Первым делом очищается холст Graphics от предыдущего кадра.

this.graphics.clear();

Затем задаётся стиль линии и рисуется сама кривая с помощью метода draw. Второй аргумент (64) определяет количество отрезков, из которых будет состоять сглаженная кривая — чем больше, тем она плавнее.

this.graphics.lineStyle(1, 0xffffff, 1);
this.curve.draw(this.graphics, 64);

После этого отрисовываются зелёные точки — опорные узлы сплайна.

this.graphics.fillStyle(0x00ff00, 1);
for (let i = 0; i < this.points.length; i++)
{
    this.graphics.fillCircle(this.points[i].x, this.points[i].y, 4);
}

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

Самая важная для геймдева часть — получение координат точки на кривой в зависимости от прогресса `t. Это делает методgetPoint`.

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

Он принимает текущее значение `t(от 0 до 1) и вектор (this.path.vec`), в который запишутся вычисленные координаты X и Y. Эти координаты затем используются, чтобы нарисовать красный маркер, движущийся по пути.

this.graphics.fillStyle(0xff0000, 1);
this.graphics.fillCircle(this.path.vec.x, this.path.vec.y, 8);

Именно этот механизм — getPoint — вы будете использовать, чтобы привязать к кривой спрайт врага, снаряда или камеры.

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

Класс Phaser.Curves.Spline — это мощный и простой инструмент для работы с плавными путями. Вы можете динамически менять массив опорных точек, создавая изменяемые маршруты, или использовать несколько кривых для сложных траекторий. Для экспериментов попробуйте: 1. Привязать к this.path.vec спрайт, а не точку отрисовки. 2. Изменять массив points в реальном времени в ответ на действия игрока. 3. Использовать getPoint для расчёта позиции нескольких объектов, создавая, например, строй врагов.