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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    create ()
    {
        const polygon = new Phaser.Geom.Polygon([
            0, 143,
            0, 92,
            110, 40,
            244, 4,
            330, 0,
            458, 12,
            574, 18,
            600, 79,
            594, 153,
            332, 152,
            107, 157
        ]);

        const graphics = this.add.graphics({ x: 100, y: 200 });

        graphics.lineStyle(2, 0x00aa00);

        graphics.beginPath();

        graphics.moveTo(polygon.points[0].x, polygon.points[0].y);

        for (let i = 1; i < polygon.points.length; i++)
        {
            graphics.lineTo(polygon.points[i].x, polygon.points[i].y);
        }

        graphics.closePath();
        graphics.strokePath();
    }
}

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

const game = new Phaser.Game(config);

Создание геометрии: объект Polygon

Всё начинается с определения формы. Класс Phaser.Geom.Polygon принимает в конструктор плоский массив чисел, где каждая пара значений представляет координату (x, y) вершины многоугольника. Важно помнить, что точки задаются в локальной системе координат самого полигона, а его положение в мире сцены будет задано позже.

const polygon = new Phaser.Geom.Polygon([
    0, 143,
    0, 92,
    110, 40,
    // ... остальные точки
    107, 157
]);

В этом примере массив описывает сложную неправильную форму. Порядок перечисления точек имеет значение — они будут последовательно соединены линиями.

Подготовка графического контекста

Чтобы что-то нарисовать, нужен инструмент для рисования. В Phaser за это отвечает объект Graphics. Мы создаём его через this.add.graphics(). Передавая объект с параметрами `xиy`, мы задаём позицию начала отрисовки на сцене. Все последующие операции с этим графическим объектом будут вычисляться относительно этой точки.

const graphics = this.add.graphics({ x: 100, y: 200 });

Далее настраиваем стиль линии, которая будет обводить наш полигон. Метод lineStyle() принимает толщину линии и цвет в числовом формате (hex).

graphics.lineStyle(2, 0x00aa00);

Отрисовка контура по точкам

Отрисовка происходит в несколько этапов, имитируя работу с контекстом канваса. Сначала мы начинаем новый путь командой beginPath().

graphics.beginPath();

Затем перемещаем «виртуальное перо» к первой точке полигона, используя её координаты из свойства polygon.points. Это массив объектов со свойствами `xиy`.

graphics.moveTo(polygon.points[0].x, polygon.points[0].y);

В цикле мы проходим по всем остальным точкам полигона и командой lineTo() рисуем к ним линии от предыдущей позиции.

for (let i = 1; i < polygon.points.length; i++)
{
    graphics.lineTo(polygon.points[i].x, polygon.points[i].y);
}

Чтобы фигура была замкнутой, вызываем closePath(). Эта команда автоматически рисует линию от последней точки обратно к первой. После этого команда strokePath() применяет ранее заданный стиль линии и выводит контур на экран.

graphics.closePath();
graphics.strokePath();

Сборка и запуск сцены

Код примера оформлен как отдельная сцена Example. Для её запуска необходимо создать конфигурационный объект игры (config), указав тип рендерера, размеры холста, родительский контейнер и класс сцены. Инициализация игры с этим конфигом запускает весь процесс.

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

const game = new Phaser.Game(config);

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

Вы освоили базовый, но мощный паттерн: создание геометрического объекта данных (Polygon) и его отдельную визуализацию через Graphics. Это разделение логики и отображения открывает много возможностей. Для экспериментов попробуйте: анимировать точки полигона, создав движущуюся фигуру; использовать метод polygon.contains(x, y) для проверки попадания курсора в сложную область; залить полигон цветом с помощью graphics.fillPath(); или привязать физическое тело к форме полигона для сложных коллизий.