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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    a = 0;
    graphics;
    polygon;

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

        this.polygon = new Phaser.Geom.Polygon([
            200, 150,
            400, 300,
            600, 150,
            750, 300,
            600, 450,
            200, 450,
            50, 300
        ]);
    }

    update ()
    {
        this.a += 0.015;

        if (this.a > Math.PI * 4)
        {
            this.a -= Math.PI * 4;
        }

        const x = 400 - Math.cos(this.a / 2) * 400;
        const y = 300 - Math.sin(this.a * 2) * 300;

        this.graphics.clear();

        this.graphics.strokePoints(this.polygon.points, true);

        if (Phaser.Geom.Polygon.Contains(this.polygon, x, y))
        {
            this.graphics.fillStyle(0xaa0000);
        }
        else
        {
            this.graphics.fillStyle(0x0000aa);
        }

        this.graphics.fillCircle(x, y, 8);
    }
}

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

const game = new Phaser.Game(config);

Создание полигона и графики

Вся работа происходит внутри сцены (Scene). Сначала инициализируются поля класса: угол `aдля анимации, ссылка на объект графикиgraphicsи сам полигонpolygon`.

В методе create() создается холст для рисования — объект Graphics. Затем определяется форма полигона. Полигон в Phaser задается массивом чисел, где каждая пара значений — это координаты X и Y его вершины. Порядок обхода вершин имеет значение.

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

this.polygon = new Phaser.Geom.Polygon([
    200, 150,
    400, 300,
    600, 150,
    750, 300,
    600, 450,
    200, 450,
    50, 300
]);

Анимация точки и проверка попадания

В методе update() происходит анимация. Угол `a` постоянно увеличивается, что позволяет рассчитать координаты движущейся точки по формулам окружности с разными коэффициентами. Это создает сложную траекторию (фигуру Лиссажу).

Перед каждой отрисовкой холст очищается методом clear(), и заново рисуется контур полигона с помощью strokePoints(). Ключевой момент — вызов Phaser.Geom.Polygon.Contains(). Этот статический метод принимает полигон и координаты точки (x, y), возвращая true или false.

const x = 400 - Math.cos(this.a / 2) * 400;
const y = 300 - Math.sin(this.a * 2) * 300;

this.graphics.clear();
this.graphics.strokePoints(this.polygon.points, true);

if (Phaser.Geom.Polygon.Contains(this.polygon, x, y))
{
    this.graphics.fillStyle(0xaa0000); // Красный цвет, если точка ВНУТРИ
}
else
{
    this.graphics.fillStyle(0x0000aa); // Синий цвет, если точка СНАРУЖИ
}

this.graphics.fillCircle(x, y, 8);

В зависимости от результата проверки цвет точки меняется, что дает визуальную обратную связь.

Как использовать в реальном проекте

В игре вам вряд ли понадобится анимировать точку по сложной траектории. Чаще всего вы будете проверять позицию игрока (sprite.x, sprite.y) или координаты клика мыши.

1. **Создание зоны:** Определите полигон, описывающий область (например, ядовитое болото или зону ускорения). 2. **Проверка в update:** В основном игровом цикле проверяйте, находится ли спрайт игрока внутри этой зоны. 3. **Реакция:** При смене состояния (вошел/вышел) запускайте игровые события: наносите урон, меняйте физику, проигрывайте звук.

// Пример: проверка позиции игрока каждый кадр
update() {
    if (Phaser.Geom.Polygon.Contains(this.dangerZonePolygon, this.player.x, this.player.y)) {
        this.player.takeDamage(1);
    }
}
// Пример: реакция на клик внутри области
this.input.on('pointerdown', (pointer) => {
    if (Phaser.Geom.Polygon.Contains(this.uiButtonPolygon, pointer.x, pointer.y)) {
        this.openMenu();
    }
});

Важные нюансы работы с полигонами

Эффективность алгоритма проверки (Contains) зависит от количества вершин полигона. Для очень сложных фигур с сотнями точек частые проверки каждого кадра могут стать узким местом производительности.

- **Производительность:** Для статических зон кэшируйте результат проверки, если позиция объекта не изменилась. - **Форма полигона:** Полигон должен быть **простым** (без самопересечений) и **выпуклым или вогнутым**. Метод Contains работает с любыми простыми полигонами, но для вогнутых форм логика внутри него сложнее. - **Замыкание контура:** При создании полигона из точек методом strokePoints или fillPoints параметр closeShape (второй аргумент) должен быть true, чтобы линия вернулась к первой точке. - **Координаты:** Убедитесь, что координаты полигона и проверяемой точки находятся в одной системе координат (чаще всего — мировая система координат сцены).

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

Метод Phaser.Geom.Polygon.Contains — это мощный и простой инструмент для работы с произвольными игровыми зонами. Он снимает необходимость писать сложные геометрические алгоритмы с нуля. **Идеи для экспериментов:** 1. Создайте мини-карту с несколькими полигонами-зонами (лес, вода, город) и меняйте поведение игрока при входе в каждую. 2. Реализуйте кастомный хитбокс для большого босса сложной формы, используя полигон вместо прямоугольника. 3. Сгенерируйте полигон случайной формы (например, шум Перлина) и используйте Contains для создания «острова» с проходимыми участками.