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

При создании игр часто требуется разместить объекты вдоль пути, например, монеты на трассе или охрану по периметру. Просто взять точки из кривой недостаточно — они будут распределены неравномерно, сгущаясь на резких поворотах. В этой статье разберем, как метод `getDistancePoints` объекта `Phaser.Curves.Spline` позволяет легко получить набор точек, отстоящих друг от друга на заданное расстояние, что идеально подходит для расстановки игровых элементов.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    graphics;
    size = 32;
    points;
    curve;

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

        this.curve = new Phaser.Curves.Spline([
            50, 300,
            164, 246,
            274, 342,
            412, 257,
            522, 341,
            664, 264
        ]);

        this.points = this.curve.getDistancePoints(this.size);
    }

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

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

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

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

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

        for (let i = 0; i < this.points.length; i++)
        {
            const p = this.points[i];

            this.graphics.fillCircle(p.x, p.y, 2);

            // var x = p.x - (size / 2);
            // var y = p.y - (size / 2);
            // graphics.strokeRect(x, y, size, size);
        }
    }
}

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

const game = new Phaser.Game(config);

Создание сплайна и подготовка

Работа начинается с создания сплайновой кривой. В Phaser 3 для этого используется класс Phaser.Curves.Spline. Ему передается массив чисел, где каждая пара представляет координаты (x, y) опорной точки. Кривая автоматически проводится через эти точки.

this.curve = new Phaser.Curves.Spline([
    50, 300,
    164, 246,
    274, 342,
    412, 257,
    522, 341,
    664, 264
]);

После создания кривой мы сразу вычисляем массив точек, равноотстоящих друг от друга, с помощью ключевого метода getDistancePoints. Ему передается желаемое расстояние между точками.

this.size = 32;
this.points = this.curve.getDistancePoints(this.size);

Здесь this.size = 32 означает, что расстояние между соседними точками вдоль кривой будет примерно 32 пикселя. Метод возвращает массив объектов Phaser.Math.Vector2.

Визуализация в методе update

Для анимации или динамического отображения вся отрисовка происходит в методе update. Первым делом очищаем холст графики от предыдущего кадра, чтобы избежать наложения.

this.graphics.clear();

Затем настраиваем стиль линии и рисуем саму сплайновую кривую для наглядности. Метод draw отрисует кривую, разбив ее на 64 сегмента для плавности.

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

Далее меняем стиль на зеленый для отрисовки рассчитанных точек. Сначала устанавливаем стиль заливки для кружков, а затем — стиль линии (он может пригодиться для отладки, например, рисования квадратов).

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

Отрисовка равноотстоящих точек

Имея массив точек this.points, мы проходим по нему в цикле. Для каждой точки `pмы рисуем маленький зеленый кружок с помощьюfillCircle`.

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

Этот простой цикл визуализирует результат работы getDistancePoints. В закомментированном коде показан альтернативный вариант отладки — рисование квадратов размером this.size вокруг каждой точки. Это наглядно демонстрирует, что расстояние между *центрами* точек действительно равно заданному значению.

// var x = p.x - (size / 2);
// var y = p.y - (size / 2);
// graphics.strokeRect(x, y, size, size);

Практическое применение в играх

Метод getDistancePoints — это не просто инструмент для визуализации. Его основная сила — в геймдизайне и логике.

* **Расстановка коллекционных предметов:** Размещайте монеты, патроны или бонусы на извилистой дорожке ровными рядами. * **Патрулирование врагов:** Задайте точкам, полученным от кривой, статус "контрольных". Пусть стражник перемещается от одной точки к следующей по циклу, создавая предсказуемый, но визуально сложный маршрут. * **Траектория движения:** Используйте массив точек как готовый путь для объекта, движущегося с постоянной скоростью. Это проще, чем вычислять позицию на кривой вручную.

Ключевое преимущество перед использованием getPoints — равномерность. getPoints возвращает точки, равномерно распределенные по *параметру t* кривой, что на резких изгибах дает их сгущение. getDistancePoints гарантирует равномерность по *длине пути*, что почти всегда является желаемым поведением в игре.

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

Метод getDistancePoints — это мощный и простой способ превратить любую сплайновую кривую в удобный маршрут для игровых объектов. Он решает проблему неравномерного распределения, позволяя сосредоточиться на дизайне уровней, а не на математических расчетах. **Идеи для экспериментов:** 1. Динамически меняйте расстояние this.size в зависимости от скорости игрока, чтобы создавать разреженные или плотные группы объектов. 2. Вместо отрисовки кружков создавайте в этих точках спрайты (this.add.sprite) и анимируйте их. 3. Скомбинируйте несколько кривых и их точки, чтобы создать сложную сеть патрулей или разветвленную трассу для гонок.