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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    create ()
    {
        const graphics = this.add.graphics();

        const p0 = new Phaser.Math.Vector2(100, 500);
        const p1 = new Phaser.Math.Vector2(50, 100);
        const p2 = new Phaser.Math.Vector2(600, 100);
        const p3 = new Phaser.Math.Vector2(700, 500);

        const curve = new Phaser.Curves.CubicBezier(p0, p1, p2, p3);

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

        curve.draw(graphics, 64);

        //  Get 32 points from the curve
        const points = curve.getPoints(32);

        //  Draw the points
        graphics.fillStyle(0x00ff00, 1);

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

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

const game = new Phaser.Game(config);

Создание кривой: четыре точки судьбы

Кубическая кривая Безье определяется четырьмя опорными точками. Две из них — это начало и конец кривой (p0 и p3). Две другие (p1 и p2) — это контрольные точки, которые "притягивают" кривую, задавая её форму. В Phaser для этого используется класс Phaser.Curves.CubicBezier.

const p0 = new Phaser.Math.Vector2(100, 500);
const p1 = new Phaser.Math.Vector2(50, 100);
const p2 = new Phaser.Math.Vector2(600, 100);
const p3 = new Phaser.Math.Vector2(700, 500);

const curve = new Phaser.Curves.CubicBezier(p0, p1, p2, p3);

Здесь мы создаём кривую, которая начинается внизу слева (100, 500), резко уходит вверх к контрольным точкам, а затем плавно опускается вниз справа (700, 500).

Визуализация: рисуем кривую на сцене

Чтобы увидеть нашу кривую, нужно её нарисовать. Для этого используется объект Graphics. Мы задаём стиль линии и метод draw самой кривой, который принимает контекст отрисовки и количество отрезков для аппроксимации кривой.

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

Параметр 64 в методе curve.draw указывает, на сколько линейных сегментов разбить кривую для отрисовки. Чем выше значение, тем более гладкой будет выглядеть кривая, особенно если она сложная.

Получение точек: ключ к интерактивности

Самая мощная часть API — метод getPoints(divisions). Он возвращает массив объектов Vector2, равномерно распределённых по длине кривой. Количество точек равно параметру divisions + 1.

const points = curve.getPoints(32);

Этот вызов вернёт 33 точки (32 сегмента + начальная точка). Эти точки — не просто для отображения. Они представляют собой дискретное представление траектории, которое можно использовать для: * Пошагового перемещения объекта по кривой. * Проверки столкновений объекта с элементами на пути. * Размещения нескольких объектов (например, монет или врагов) вдоль пути.

Практическое применение: от траектории до игры

Давайте нарисуем полученные точки, чтобы визуализировать результат. Это поможет понять, как объект будет "шагать" по пути.

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

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

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

Кубические кривые Безье в Phaser — это мощный инструмент для работы с нелинейными траекториями. Метод getPoints превращает математическую абстракцию в практический набор данных для игровой логики. Для экспериментов попробуйте: анимировать движение спрайта по точкам, изменять контрольные точки в реальном времени в ответ на действия игрока или создать систему пулевого расчёта, где траектория снаряда рассчитывается один раз в виде точек для оптимизации.