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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    t = 0.5;
    interpolatedPoint;
    point2;
    point1;
    graphics;

    create ()
    {
        this.graphics = this.add.graphics({ lineStyle: { width: 3, color: 0x2266aa }, fillStyle: { color: 0x2266aa } });

        this.point1 = new Phaser.Math.Vector2(400, 300);

        this.point2 = new Phaser.Math.Vector2(550, 300);

        this.interpolatedPoint = this.point1.clone().lerp(this.point2, this.t);

        this.input.on('pointermove', pointer =>
        {

            this.point2.copy(pointer);

        });
    }

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

        this.t = (this.t + 0.01) % 1;

        this.interpolatedPoint = this.point1.clone().lerp(this.point2, this.t);

        this.graphics.fillPointShape(this.point1, 20);
        this.graphics.fillPointShape(this.point2, 20);

        this.graphics.fillStyle(0x00aa00);
        this.graphics.fillPointShape(this.interpolatedPoint, 20);

        this.graphics.lineBetween(this.point1.x, this.point1.y, this.point2.x, this.point2.y);
    }
}

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

const game = new Phaser.Game(config);

Инициализация точек и графики

В начале кода в классе сцены объявляются свойства для хранения двух точек, интерполированной точки, графического объекта и коэффициента интерполяции `t`.

В методе create() создаётся графический контекст this.graphics для рисования. Инициализируются две точки: point1 — фиксированная точка в центре экрана, point2 — изначально расположена правее. Интерполированная точка вычисляется сразу при старте с помощью метода .lerp(). Также устанавливается обработчик события движения указателя, который обновляет позицию point2.

this.graphics = this.add.graphics({ lineStyle: { width: 3, color: 0x2266aa }, fillStyle: { color: 0x2266aa } });

this.point1 = new Phaser.Math.Vector2(400, 300);

this.point2 = new Phaser.Math.Vector2(550, 300);

this.interpolatedPoint = this.point1.clone().lerp(this.point2, this.t);

this.input.on('pointermove', pointer => {
    this.point2.copy(pointer);
});

Как работает метод lerp

Метод .lerp(target, t) объекта Vector2 вычисляет линейную интерполяцию между текущей точкой и целевой точкой target. Параметр `t— это коэффициент интерполяции, обычно в диапазоне от 0 до 1. Когдаt = 0, результатом будет исходная точка; когдаt = 1` — целевая точка. Все промежуточные значения дают точку на прямой между ними.

Важно отметить, что оригинальный метод .lerp() изменяет текущий вектор. В нашем примере используется цепочка clone().lerp(...), чтобы сначала создать копию точки point1, а затем интерполировать её в сторону point2, не изменяя исходные данные.

// Прямое использование (изменяет вектор a)
a.lerp(b, 0.5);

// Безопасное использование с клонированием (как в примере)
let result = a.clone().lerp(b, 0.5);

Динамическое обновление и отрисовка

В методе update() происходит анимация. Сначала очищается холст от предыдущего кадра. Коэффициент `tциклически увеличивается от 0 до 1, создавая непрерывное движение промежуточной точки отpoint1кpoint2и обратно. На каждом кадре заново вычисляется позицияinterpolatedPoint`.

Затем отрисовываются три точки: две исходные — синим цветом, а интерполированная — зелёным. Также рисуется линия, соединяющая point1 и point2, чтобы визуализировать отрезок, по которому движется зелёная точка.

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

    this.t = (this.t + 0.01) % 1;

    this.interpolatedPoint = this.point1.clone().lerp(this.point2, this.t);

    this.graphics.fillPointShape(this.point1, 20);
    this.graphics.fillPointShape(this.point2, 20);

    this.graphics.fillStyle(0x00aa00);
    this.graphics.fillPointShape(this.interpolatedPoint, 20);

    this.graphics.lineBetween(this.point1.x, this.point1.y, this.point2.x, this.point2.y);
}

Конфигурация игры

Стандартная конфигурация игры Phaser 3. Указываются размеры холста, тип рендерера (Phaser.AUTO выбирает между WebGL и Canvas), ID родительского HTML-элемента и класс основной сцены.

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

const game = new Phaser.Game(config);

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

Линейная интерполяция с помощью Vector2.lerp() — это мощный и простой инструмент для создания плавного движения и расчёта промежуточных позиций в Phaser. Она лежит в основе многих игровых механик. Для экспериментов попробуйте: изменить закон изменения параметра `t` (например, использовать синусоиду для колебаний), применить интерполяцию к цветам или масштабу спрайтов, или создать цепочку из нескольких точек для движения по сложному маршруту.