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

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

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

Живой запуск

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

Исходный код


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

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

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

        this.curve = new Phaser.Curves.Line(new Phaser.Math.Vector2(100, 100), new Phaser.Math.Vector2(600, 400));

        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);

        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, 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 создает такую кривую, принимая начальную и конечную точки в виде объектов Phaser.Math.Vector2.

this.curve = new Phaser.Curves.Line(
    new Phaser.Math.Vector2(100, 100),
    new Phaser.Math.Vector2(600, 400)
);

После создания кривая представляет собой математическое описание отрезка от (100, 100) до (600, 400). Мы не рисуем ее сразу, а используем как источник данных для вычисления позиций.

Подготовка данных для анимации

Для управления движением по кривой используется параметр `t(от 0 до 1), где 0 соответствует началу отрезка, а 1 — его концу. Вместо того чтобы изменятьtвручную вupdate()`, мы поручаем это системе твинов.

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

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

this.tweens.add({
    targets: this.path, // Анимируемый объект
    t: 1,              // Конечное значение свойства `t`
    ease: 'Sine.easeInOut', // Плавная функция easing
    duration: 2000,    // Длительность в миллисекундах
    yoyo: true,        // Возврат к начальному значению
    repeat: -1         // Бесконечное повторение
});

Твин автоматически обновляет this.path.t в каждом кадре, создавая плавное движение.

Визуализация кривой и точки

Метод update() вызывается каждый кадр и отвечает за отрисовку. Сначала мы очищаем холст от графики предыдущего кадра с помощью this.graphics.clear().

Затем настраиваем стиль линии и рисуем саму кривую — визуальное представление отрезка.

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

Ключевой момент — получение текущих координат точки на кривой, соответствующих значению `tиз анимированного объектаthis.path`.

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

Метод getPoint() вычисляет координаты и записывает их в переданный вектор this.path.vec. После этого мы можем использовать эти координаты для отрисовки, например, красного круга.

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

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

От теории к практике: замена круга на спрайт

Вместо рисования круга графическим контекстом вы можете перемещать по кривой игровой спрайт. Для этого в методе create() создайте спрайт, а в update() обновляйте его позицию, используя вычисленный вектор.

// В create()
this.sprite = this.add.sprite(0, 0, 'player');

// В update(), после this.curve.getPoint(...)
this.sprite.setPosition(this.path.vec.x, this.path.vec.y);

Этот подход идеально подходит для движения врагов по заданному маршруту, полета снарядов или плавного перемещения элементов интерфейса.

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

Использование Phaser.Curves.Line в связке с системой твинов предоставляет элегантный и мощный способ анимации движения по прямой. Вы получаете контроль над скоростью, плавностью и повторяемостью траектории без необходимости управлять координатами вручную. Для экспериментов попробуйте: создать несколько кривых и переключать движение между ними; использовать другие типы кривых, например Phaser.Curves.Spline, для сложных путей; или привязать к движению по кривой не только позицию, но и угол поворота спрайта, используя производную getTangent().