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

При создании игр часто возникает задача размещения объектов в пределах заданной области. Например, нужно расставить монеты внутри треугольного леса или генерировать снаряды из треугольного сектора пушки. Вручную рассчитывать координаты для таких областей сложно и неэффективно. В этой статье мы разберем, как использовать встроенные возможности геометрии Phaser для равномерной генерации случайных точек внутри треугольника, что идеально подходит для создания динамичного и разнообразного игрового пространства.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    index = 0;
    points;
    graphics;
    triangle;

    create ()
    {
        this.graphics = this.add.graphics({ lineStyle: { width: 2, color: 0xaaaa00 }, fillStyle: { color: 0xff0000 }});

        this.triangle = new Phaser.Geom.Triangle(400, 100, 100, 500, 700, 500);

        this.points = [];

        for (let i = 0; i < 25; i++)
        {
            // if we omit a parameter, new Point instance will be created and returned
            this.points.push(this.triangle.getRandomPoint());
        }
    }

    update ()
    {
        this.index = ++this.index % 25;

        // we can also supply an instance of Point that will be modified
        this.triangle.getRandomPoint(this.points[this.index]);

        this.graphics.clear();
        this.graphics.strokeTriangleShape(this.triangle);

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

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

const game = new Phaser.Game(config);

Создание геометрической фигуры

В Phaser работа с геометрией начинается с создания объекта фигуры. В нашем примере это треугольник. Класс Phaser.Geom.Triangle принимает координаты трех вершин.

this.triangle = new Phaser.Geom.Triangle(400, 100, 100, 500, 700, 500);

Здесь мы создаем треугольник с вершинами в точках (400, 100), (100, 500) и (700, 500). Эти координаты задают его форму и расположение на сцене. Для визуализации создается объект this.graphics с заданными стилями линии и заливки, который будет рисовать фигуру.

Генерация случайных точек: два способа

Основной метод, который мы используем, — getRandomPoint(). Он принадлежит объекту треугольника и имеет две формы вызова, что является удобной идиомой в Phaser API.

Первый способ — вызвать метод без параметров. В этом случае он создаст и вернет новый объект Phaser.Geom.Point со случайными координатами внутри треугольника.

// Создание нового экземпляра точки
this.points.push(this.triangle.getRandomPoint());

Этот подход используется в цикле create() для первоначального заполнения массива this.points 25-ю точками.

Второй способ — передать методу существующий объект точки в качестве параметра. Метод модифицирует координаты `xиy` этого объекта, что более эффективно с точки зрения производительности, так как избегает создания новых объектов в памяти.

// Модификация существующего экземпляра точки
this.triangle.getRandomPoint(this.points[this.index]);

Именно этот способ используется в update() для анимации, чтобы постоянно обновлять координаты одной из точек в массиве.

Визуализация и анимация

Логика отрисовки и обновления находится в методе update(), который вызывается каждый кадр.

Сначала обновляется индекс this.index для циклического перебора массива точек. Затем координаты точки с этим индексом перезаписываются новыми случайными значениями с помощью getRandomPoint(point).

this.index = ++this.index % 25;
this.triangle.getRandomPoint(this.points[this.index]);

Перед каждой отрисовкой Graphics очищается методом clear(). После этого отрисовывается контур треугольника и все 25 точек в виде закрашенных кругов.

this.graphics.clear();
this.graphics.strokeTriangleShape(this.triangle);

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

В результате мы видим статичный треугольник, внутри которого одна из 25 точек постоянно "телепортируется" в новую случайную позицию, создавая эффект мерцания.

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

Этот механизм — не просто демонстрация математики, а готовый инструмент для геймдизайна.

* **Спавн предметов:** Равномерно заполнить треугольную область ресурсами, ловушками или бонусами. * **Зона поражения:** Если треугольник задает конус атаки магии или прожектора, точки могут отмечать попадания. * **Партиклы:** Задать треугольную область эмиттера для системы частиц, чтобы дым или искры вылетали из конкретного сектора.

Ключевое преимущество — равномерное распределение. Метод getRandomPoint() гарантирует, что у точки одинаковая вероятность оказаться в любой части площади треугольника, а не только около центра. Это делает размещение объектов естественным.

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

Использование Phaser.Geom.Triangle.getRandomPoint() — это элегантный и производительный способ работы со случайностью внутри сложных областей. Он избавляет от необходимости писать собственные алгоритмы выборки. Для экспериментов попробуйте изменить форму треугольника в реальном времени в ответ на действия игрока, комбинировать несколько треугольников для создания сложных полигонов или привязать генерацию точек к таймеру для создания волн противников, выходящих из треугольного портала.