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

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

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

Живой запуск

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

Исходный код


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

        const triangles = [];

        for (let x = 0; x < 8; x++)
        {
            for (let y = 0; y < 6; y++)
            {
                const x1 = Phaser.Math.Between(4, 5) * 10 + x * 100;
                const y3 = Phaser.Math.Between(8, 9) * 10 + y * 100;

                const triangle = new Phaser.Geom.Triangle(
                    x1, 10 + y * 100,
                    10 + x * 100, 90 + y * 100,
                    90 + x * 100, y3);

                triangles.push(triangle);
            }
        }

        const pointerTriangle = new Phaser.Geom.Triangle(50, 10, 10, 90, 90, 90);

        this.input.on('pointermove', pointer =>
        {

            const x = Math.floor(pointer.x / 100);
            const y = Math.floor(pointer.y / 100);

            pointerTriangle.x1 = 50 + x * 100;
            pointerTriangle.y1 = 10 + y * 100;
            pointerTriangle.x2 = 10 + x * 100;
            pointerTriangle.y2 = 90 + y * 100;
            pointerTriangle.x3 = 90 + x * 100;
            pointerTriangle.y3 = 90 + y * 100;

            redraw();
        });

        redraw();

        function redraw ()
        {
            graphics.clear();
            graphics.lineStyle(2, 0xaaaa00);

            let strokeRed = false;

            for (let i = 0; i < triangles.length; i++)
            {
                graphics.strokeTriangleShape(triangles[i]);
                strokeRed = strokeRed || Phaser.Geom.Triangle.Equals(pointerTriangle, triangles[i]);
            }

            if (strokeRed)
            {
                graphics.lineStyle(5, 0xaa0000);
            }
            else
            {
                graphics.lineStyle(5, 0x0000aa);
            }

            graphics.strokeTriangleShape(pointerTriangle);
        }
    }
}

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

const game = new Phaser.Game(config);

Подготовка сцены и создание массива треугольников

В примере создаётся игровая сцена, на которой мы будем рисовать и сравнивать треугольники. Первым делом инициализируется объект graphics для отрисовки примитивов.

Затем в двойном цикле формируется массив из 48 треугольников (triangles). Они располагаются в сетке 8x6. Координаты вершин каждого треугольника вычисляются с небольшим случайным смещением с помощью Phaser.Math.Between, что делает демонстрацию более наглядной — треугольники не идеально выровнены.

const graphics = this.add.graphics();
const triangles = [];

for (let x = 0; x < 8; x++)
{
    for (let y = 0; y < 6; y++)
    {
        const x1 = Phaser.Math.Between(4, 5) * 10 + x * 100;
        const y3 = Phaser.Math.Between(8, 9) * 10 + y * 100;

        const triangle = new Phaser.Geom.Triangle(
            x1, 10 + y * 100,
            10 + x * 100, 90 + y * 100,
            90 + x * 100, y3);

        triangles.push(triangle);
    }
}

Создание управляемого треугольника и обработка ввода

Создаётся треугольник-курсор (pointerTriangle), который будет следовать за указателем мыши. Изначально он задаётся в координатах первой ячейки сетки.

На событие pointermove вешается обработчик. Он вычисляет, в какую ячейку сетки попал курсор, и пересчитывает координаты pointerTriangle, "привязывая" его к этой ячейке. После обновления позиции вызывается функция redraw() для перерисовки всего холста.

const pointerTriangle = new Phaser.Geom.Triangle(50, 10, 10, 90, 90, 90);

this.input.on('pointermove', pointer =>
{
    const x = Math.floor(pointer.x / 100);
    const y = Math.floor(pointer.y / 100);

    pointerTriangle.x1 = 50 + x * 100;
    pointerTriangle.y1 = 10 + y * 100;
    pointerTriangle.x2 = 10 + x * 100;
    pointerTriangle.y2 = 90 + y * 100;
    pointerTriangle.x3 = 90 + x * 100;
    pointerTriangle.y3 = 90 + y * 100;

    redraw();
});

Логика сравнения и визуализация в функции redraw

Функция redraw() — сердце примера. Она очищает холст и заново рисует все треугольники. Ключевой момент происходит в цикле перебора массива triangles.

Для каждого треугольника из массива вызывается метод Phaser.Geom.Triangle.Equals, который сравнивает его с текущим положением pointerTriangle. Метод возвращает true только если все три вершины треугольников попарно равны (имеют одинаковые координаты `xиy`).

Флаг strokeRed становится true, если хотя бы один треугольник в массиве совпал с курсорным. В зависимости от этого флага меняется стиль линии (толщина и цвет) для отрисовки самого pointerTriangle. Это даёт мгновенную визуальную обратную связь.

function redraw ()
{
    graphics.clear();
    graphics.lineStyle(2, 0xaaaa00);

    let strokeRed = false;

    for (let i = 0; i < triangles.length; i++)
    {
        graphics.strokeTriangleShape(triangles[i]);
        strokeRed = strokeRed || Phaser.Geom.Triangle.Equals(pointerTriangle, triangles[i]);
    }

    if (strokeRed)
    {
        graphics.lineStyle(5, 0xaa0000);
    }
    else
    {
        graphics.lineStyle(5, 0x0000aa);
    }

    graphics.strokeTriangleShape(pointerTriangle);
}

Важные детали работы метода Equals

Метод Phaser.Geom.Triangle.Equals выполняет точное сравнение координат. Это важно понимать: треугольник со вершинами (0,0, 10,0, 0,10) не будет считаться равным треугольнику (0,0, 0,10, 10,0), хотя они конгруэнтны — важен порядок вершин. В данном примере совпадение возможно, потому что оба треугольника создаются по одинаковому шаблону для одной ячейки сетки.

Для сравнения треугольников с учётом погрешности (например, при проверке столкновений) в Phaser существуют другие методы, например, Phaser.Geom.Triangle.Contains. Метод Equals же идеален для случаев, когда нужно проверить, ссылаются ли две переменные на один и тот же геометрический объект или на объекты с идентичными координатами.

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

Метод Phaser.Geom.Triangle.Equals — это простой и эффективный инструмент для точного сравнения треугольников. Показанный пример можно адаптировать для систем выделения объектов, проверки правильности сборки пазла или активации триггеров в игре. Для экспериментов попробуйте изменить логику: сравнивайте pointerTriangle не со всеми треугольниками, а только с ближайшим, используя Phaser.Geom.Triangle.Center, или добавьте проверку на приблизительное равенство с заданной дельтой.