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

Визуализация случайных точек на линии — это не просто демонстрация математики, а мощный инструмент для создания игровых механик. Вы можете использовать этот подход для расстановки монет на трассе, определения траектории полёта снарядов или случайного появления врагов вдоль маршрута. В этой статье мы разберём пример из официальной документации Phaser и покажем, как метод `getRandomPoint()` объекта `Phaser.Geom.Line` может оживить вашу игру.

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

Живой запуск

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

Исходный код


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

    create ()
    {
        this.line = new Phaser.Geom.Line(100, 500, 700, 100);

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

        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.line.getRandomPoint());
        }
    }

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

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

        this.graphics.clear();
        this.graphics.strokeLineShape(this.line);

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

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

const game = new Phaser.Game(config);

Создание линии и подготовка данных

Всё начинается с определения линии и подготовки массива для хранения точек. Линия создаётся между двумя координатами, а графический контекст (graphics) настраивается для отрисовки.

this.line = new Phaser.Geom.Line(100, 500, 700, 100);
this.graphics = this.add.graphics({ lineStyle: { width: 2, color: 0xff00ff }, fillStyle: { color: 0xff0000 }});
this.points = [];

Здесь мы создаём линию от точки (100, 500) до (700, 100). Объект graphics будет рисовать саму линию розовым цветом и точки красным квадратиком. Массив points изначально пуст — в него мы будем складывать сгенерированные точки.

Генерация начального набора точек

Перед началом цикла игры нужно создать начальный набор из 25 случайных точек на линии. Для этого в цикле вызывается метод getRandomPoint().

for (let i = 0; i < 25; i++)
{
    // если не передавать параметр, метод создаст и вернёт новый экземпляр Point
    this.points.push(this.line.getRandomPoint());
}

Ключевой момент: если вызвать getRandomPoint() без аргументов, метод сам создаст новый объект Phaser.Geom.Point с случайными координатами на линии и вернёт его. Это удобно для быстрой инициализации массива.

Анимация и обновление точек

В методе update() происходит магия анимации. Каждый кадр мы обновляем координаты одной из точек, перерисовывая графику.

this.index = ++this.index % 25;
// мы также можем передать экземпляр Point, который будет изменён
this.line.getRandomPoint(this.points[this.index]);

Переменная index циклически перебирает индексы массива от 0 до 24. Вторая форма вызова getRandomPoint(point) принимает существующий объект Point и модифицирует его координаты, помещая в новую случайную позицию на той же линии. Это эффективно, так как не создаёт новых объектов в памяти каждый кадр.

Отрисовка линии и точек

После обновления данных необходимо очистить холст и заново нарисовать линию со всеми точками.

this.graphics.clear();
this.graphics.strokeLineShape(this.line);

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

Метод clear() стирает всё, что было нарисовано этим графическим объектом. Затем strokeLineShape(this.line) рисует контур линии. В цикле для каждой точки рисуется закрашенный квадрат с центром в её координатах (смещение на -4 по осям и размер 8x8 даёт квадрат 8 на 8 пикселей).

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

Как использовать эту технику не в демо, а в реальной игре? Вот несколько идей:

* **Маршруты патрулирования:** Задайте линию как путь для врага. Каждые несколько секунд обновляйте его целевую точку с помощью getRandomPoint(), создавая непредсказуемый патрульный маршрут. * **Разброс снарядов:** При стрельбе из дробовика задайте основную линию траектории. Затем сгенерируйте несколько случайных точек на ней или рядом (используя смещение) для определения направления каждого дробина. * **Генерация ресурсов:** Разместите линию вдоль горного хребта или реки. В начале уровня сгенерируйте на ней случайные точки — это будут места появления руды или рыбы.

// Пример: создание цели для патруля врага
update() {
    if (this.patrolTimer <= 0) {
        // Обновляем целевую точку патруля раз в 2 секунды
        this.line.getRandomPoint(this.enemy.target);
        this.patrolTimer = 2000;
    }
    this.patrolTimer -= this.game.loop.delta;
}

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

Метод Phaser.Geom.Line.getRandomPoint() — это элегантный и производительный способ работы со случайными позициями на отрезке. Он позволяет избежать лишних вычислений и создания объектов, что критично для производительности игры. Для экспериментов попробуйте: анимировать не одну, а несколько точек одновременно с разной скоростью; использовать не прямую, а ломаную линию, разбив её на сегменты; или применить этот принцип к другим геометрическим объектам Phaser, например, для получения случайной точки внутри прямоугольника (Phaser.Geom.Rectangle.getRandomPoint).