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

В играх часто требуется определить, пересекаются ли объекты. Для проверки столкновений пуль, траекторий движения или границ уровня можно использовать линии. Встроенный модуль геометрии Phaser позволяет легко и эффективно проверять пересечение двух линий и находить точку, где это происходит. Этот подход гораздо производительнее, чем расчеты через физические тела, и идеально подходит для кастомных систем коллизий, визуализации лучей (ray casting) или редакторов уровней.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    graphics;
    line2;
    line1;

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

        this.line1 = new Phaser.Geom.Line(260, 200, 450, 450);
        this.line2 = new Phaser.Geom.Line(300, 400, 500, 500);

        this.graphics.lineStyle(2, 0x00ff00);
        this.graphics.strokeLineShape(this.line1);
        this.graphics.lineStyle(2, 0xffff00);
        this.graphics.strokeLineShape(this.line2);

        this.input.on('pointermove', pointer =>
        {
            this.line2.x2 = pointer.x;
            this.line2.y2 = pointer.y;
        });
    }

    update ()
    {
        Phaser.Geom.Line.Rotate(this.line1, 0.02);

        this.graphics.clear();
        this.graphics.fillStyle(0xffffff);
        this.graphics.lineStyle(2, 0x00ff00);
        this.graphics.strokeLineShape(this.line1);

        const p = { x: 0, y: 0 };

        if (Phaser.Geom.Intersects.LineToLine(this.line1, this.line2, p))
        {
            this.graphics.lineStyle(2, 0xff0000);
            this.graphics.fillPointShape(p, 8);
        }
        else
        {
            this.graphics.lineStyle(2, 0xffff00);
        }

        this.graphics.strokeLineShape(this.line2);
    }
}

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

const game = new Phaser.Game(config);

Создание сцены и инициализация линий

В методе create() инициализируется графика и две линии. Линия в Phaser определяется начальной и конечной точками (x1, y1, x2, y2). Первая линия (line1) статична по своим координатам, но будет вращаться. Вторая линия (line2) жестко закреплена в начальной точке, а ее конечная точка будет следовать за курсором мыши.

Используется объект Graphics для рисования примитивов. Сначала задается стиль линии, а затем она отрисовывается.

this.graphics = this.add.graphics();
this.line1 = new Phaser.Geom.Line(260, 200, 450, 450);
this.line2 = new Phaser.Geom.Line(300, 400, 500, 500);
this.graphics.lineStyle(2, 0x00ff00);
this.graphics.strokeLineShape(this.line1);
this.graphics.lineStyle(2, 0xffff00);
this.graphics.strokeLineShape(this.line2);

Слушатель события pointermove обновляет координаты конечной точки второй линии, делая ее интерактивной.

this.input.on('pointermove', pointer =>
{
    this.line2.x2 = pointer.x;
    this.line2.y2 = pointer.y;
});

Игровой цикл: вращение и проверка пересечения

В методе update() происходит основная логика. Сначала первая линия вращается вокруг своей начальной точки с помощью статического метода Phaser.Geom.Line.Rotate().

Phaser.Geom.Line.Rotate(this.line1, 0.02);

Затем холст Graphics полностью очищается, и первая линия перерисовывается. Ключевой этап — проверка пересечения с помощью Phaser.Geom.Intersects.LineToLine(). Этот метод принимает две линии и объект-результат `p`.

const p = { x: 0, y: 0 };
if (Phaser.Geom.Intersects.LineToLine(this.line1, this.line2, p))

Если линии пересекаются, метод возвращает true и записывает координаты точки пересечения в переданный объект `p. В противном случае возвращаетсяfalse`.

Важно: объект `pдолжен быть создан заранее и передан в метод третьим аргументом. Метод изменит его свойстваxиy` только в случае успешного обнаружения пересечения.

Визуальная обратная связь

На основе результата проверки меняется визуальное отображение. Если пересечение есть, стиль второй линии меняется на красный, а в вычисленной точке `p` рисуется закрашенная точка.

if (Phaser.Geom.Intersects.LineToLine(this.line1, this.line2, p))
{
    this.graphics.lineStyle(2, 0xff0000);
    this.graphics.fillPointShape(p, 8);
}
else
{
    this.graphics.lineStyle(2, 0xffff00);
}
this.graphics.strokeLineShape(this.line2);

Точка рисуется методом fillPointShape(), который принимает объект с координатами и размер. Так пользователь сразу видит, когда линии пересекаются и где именно находится точка контакта. Это полезно для отладки и создания наглядных демонстраций.

Конфигурация игры и запуск

Стандартная конфигурация игры Phaser 3. Ключевые параметры: размер холста и указание на класс сцены, который содержит всю логику.

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

Класс Example становится активной сценой игры. Весь код, описанный выше, выполняется в контексте этой сцены и ее методов жизненного цикла (create, update).

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

Метод Phaser.Geom.Intersects.LineToLine — мощный и легковесный инструмент для проверки пересечений. Он идеален для нефизических коллизий, например, для определения, пересек ли луч (Line) зону видимости врага или границу сектора. Для экспериментов попробуйте

  1. Сделать первую линию тоже подвижной, управляемой с клавиатуры
  2. Создать массив линий-препятствий и проверять пересечение с ними всех пуль
  3. Использовать точку пересечения `p` для запуска частиц или звукового эффекта при "попадании"