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

При работе с геометрией в играх часто возникает задача определить границы произвольной формы. Например, для проверки видимости объекта, грубой проверки столкновений или оптимизации рендеринга. Phaser предоставляет удобный метод `Phaser.Geom.Polygon.GetAABB()` для вычисления Axis-Aligned Bounding Box (AABB) — ограничивающего прямоугольника, выровненного по осям координат, для любого полигона. Эта статья на практическом примере покажет, как в реальном времени получать AABB для динамического полигона, вершины которого можно перемещать и добавлять. Вы научитесь эффективно работать с геометрией для создания интерактивных игровых механик.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    create ()
    {
        const graphics = this.add.graphics({ lineStyle: { width: 2, color: 0xaa6622 } });

        const points = [
            new Phaser.Math.Vector2(220, 450),
            new Phaser.Math.Vector2(200, 200),
            new Phaser.Math.Vector2(400, 300)
        ];

        const polygon = new Phaser.Geom.Polygon(points);

        const aabb = Phaser.Geom.Polygon.GetAABB(polygon);

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

            points[points.length - 1].copy(pointer);

            polygon.setTo(points);

            Phaser.Geom.Polygon.GetAABB(polygon, aabb);

            redraw();
        });

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

            points.push(points[points.length - 1].clone());

        });

        redraw();

        function redraw ()
        {
            graphics.clear();

            graphics.strokePoints(polygon.points, true);

            graphics.lineStyle(2, 0x0000aa);
            graphics.strokeRectShape(aabb);
        }
    }
}

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

const game = new Phaser.Game(config);

Что такое AABB и зачем он нужен?

Axis-Aligned Bounding Box (AABB) — это минимальный прямоугольник, стороны которого параллельны осям X и Y, полностью содержащий внутри себя заданную геометрическую фигуру. Он является фундаментальной концепцией в компьютерной графике и игровой физике.

Основные применения в играх: * **Оптимизация столкновений:** Проверка пересечения AABB двух объектов выполняется гораздо быстрее, чем проверка столкновения сложных полигонов. Это идеальный первый, «грубый» проход. * **Определение видимости (Frustum Culling):** Камера может быстро отбросить объекты, чьи AABB находятся за пределами видимой области. * **Пространственное разбиение:** Используется в алгоритмах, таких как QuadTree или Grid, для эффективного поиска объектов в пространстве.

В Phaser AABB представлен объектом типа Phaser.Geom.Rectangle.

Создание полигона и вычисление AABB

В примере сначала создается полигон на основе массива точек (векторов). Затем для него вычисляется статический AABB. Ключевой метод — Phaser.Geom.Polygon.GetAABB(). Он может работать в двух режимах: создать новый объект прямоугольника или перезаписать существующий (что полезно для оптимизации, чтобы избежать создания новых объектов в цикле).

const points = [
    new Phaser.Math.Vector2(220, 450),
    new Phaser.Math.Vector2(200, 200),
    new Phaser.Math.Vector2(400, 300)
];

const polygon = new Phaser.Geom.Polygon(points);

// Первый вызов: создаем новый объект AABB
const aabb = Phaser.Geom.Polygon.GetAABB(polygon);

Динамическое обновление и перерисовка

Интерактивность достигается за счет обработки событий указателя. При движении мыши (pointermove) последняя вершина полигона перемещается в позицию курсора. Важный момент: мы не создаем новый полигон каждый раз, а обновляем существующий с помощью метода polygon.setTo(). Это эффективнее.

Затем AABB пересчитывается. Обратите внимание на второй аргумент метода GetAABB — это уже существующий объект aabb, в который будет записан новый результат. Это предотвращает утечку памяти из-за постоянного создания объектов.

this.input.on('pointermove', pointer => {
    // Перемещаем последнюю точку
    points[points.length - 1].copy(pointer);
    // Обновляем полигон новыми точками
    polygon.setTo(points);
    // Пересчитываем AABB, используя тот же объект 'aabb'
    Phaser.Geom.Polygon.GetAABB(polygon, aabb);
    redraw();
});

Добавление новых вершин и отрисовка

По клику мыши (pointerdown) в полигон добавляется новая вершина. Для этого в массив points клонируется последняя существующая точка. Клон создается, чтобы у новой точки была своя независимая ссылка.

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

this.input.on('pointerdown', pointer => {
    // Добавляем новую точку, клонируя последнюю
    points.push(points[points.length - 1].clone());
});

function redraw () {
    graphics.clear();
    // Рисуем полигон
    graphics.strokePoints(polygon.points, true);
    // Меняем стиль линии и рисуем AABB
    graphics.lineStyle(2, 0x0000aa);
    graphics.strokeRectShape(aabb);
}

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

Метод Phaser.Geom.Polygon.GetAABB() — это мощный и оптимизированный инструмент для работы с границами сложных форм. Используя перезапись существующего объекта прямоугольника, вы можете создавать высокопроизводительные интерактивные системы даже на мобильных устройствах. **Идеи для экспериментов:** 1. Реализуйте простую проверку столкновений двух полигонов, сначала сравнив их AABB. 2. Создайте камеру, которая будет отсекать объекты за пределами экрана на основе их AABB. 3. Добавьте физическое тело (Physics Body) и свяжите его размеры с динамически обновляемым AABB вашего полигона для нетривиальной формы столкновений.