О чем этот пример
В играх часто возникает необходимость определить, находится ли курсор мыши или координата объекта внутри сложной фигуры. Это может быть полезно для реализации интерактивных зон, кастомных кнопок сложной формы (например, шестиугольники в стратегических играх) или невидимых триггеров. Встроенная система физики Phaser отлично работает с прямоугольниками и кругами, но для произвольных полигонов требуется отдельное решение. В этой статье мы разберем, как использовать геометрический модуль Phaser для точной проверки попадания точки в многоугольник любой формы.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
create ()
{
const graphics = this.add.graphics();
const polygon = new Phaser.Geom.Polygon([
200, 150,
400, 300,
600, 150,
750, 300,
600, 450,
200, 450,
50, 300
]);
graphics.fillStyle(0x00aa00);
graphics.fillPoints(polygon.points, true);
this.input.on('pointermove', pointer =>
{
graphics.clear();
if (Phaser.Geom.Polygon.ContainsPoint(polygon, pointer))
{
graphics.fillStyle(0xaa0000);
}
else
{
graphics.fillStyle(0x00aa00);
}
graphics.fillPoints(polygon.points, true);
});
}
}
const config = {
width: 800,
height: 600,
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Создание полигона и его отрисовка
Вся логика в Phaser размещается внутри методов сцены (Scene). В данном примере работа начинается в методе create. Первым делом мы создаем объект Graphics, который предназначен для программной отрисовки примитивов.
const graphics = this.add.graphics();
Далее мы определяем форму нашего полигона. Класс Phaser.Geom.Polygon принимает в конструктор плоский массив чисел, где каждая пара значений представляет собой координату X и Y вершины многоугольника. Порядок вершин важен — они должны следовать по контуру фигуры.
const polygon = new Phaser.Geom.Polygon([
200, 150,
400, 300,
600, 150,
750, 300,
600, 450,
200, 450,
50, 300
]);
После создания полигона мы его отрисовываем. Устанавливаем зеленый цвет заливки и вызываем метод fillPoints. Первый аргумент — массив точек полигона (получаем через свойство polygon.points), второй аргумент true указывает, что фигура должна быть замкнута (последняя точка соединится с первой).
graphics.fillStyle(0x00aa00);
graphics.fillPoints(polygon.points, true);
Обработка движения курсора и проверка попадания
Чтобы сделать полигон интерактивным, нужно отслеживать движение мыши. В Phaser для этого используется объект this.input и его событие 'pointermove'.
this.input.on('pointermove', pointer =>
{
// Код обработчика
});
Каждый раз при движении мыши вызывается функция-обработчик. Ей передается объект pointer, который содержит текущие координаты курсора (pointer.x и pointer.y).
Первое действие внутри обработчика — очистка холста Graphics от предыдущего кадра. Без этого старые отрисованные фигуры будут накладываться на новые.
graphics.clear();
Затем происходит ключевая операция — проверка, находится ли точка курсора внутри полигона. Для этого используется статический метод Phaser.Geom.Polygon.ContainsPoint. Он принимает два аргумента: объект полигона и объект точки (в нашем случае pointer, который также является объектом с координатами). Метод возвращает true или false.
if (Phaser.Geom.Polygon.ContainsPoint(polygon, pointer))
{
graphics.fillStyle(0xaa0000);
}
else
{
graphics.fillStyle(0x00aa00);
}
В зависимости от результата проверки мы меняем цвет заливки: красный, если курсор внутри полигона, и зеленый, если снаружи.
Оптимизация и перерисовка
После определения цвета мы заново рисуем полигон с обновленным стилем. Это происходит в каждом кадре, пока движется мышь, создавая эффект динамической подсветки области.
graphics.fillPoints(polygon.points, true);
Важно понимать, что метод ContainsPoint выполняет математический расчет (например, алгоритм "луч") для произвольного полигона. Это эффективно, но для очень сложных фигур с сотнями вершин или при проверке множества точек каждый кадр это может стать узким местом производительности. В таких случаях стоит рассмотреть использование физических тел (Body), предварительную оптимизацию формы или пространственное разбиение.
Конфигурация игры (config) и ее запуск стандартны для Phaser. Обратите внимание, что в конфиге указан наш класс Example в качестве сцены.
const config = {
width: 800,
height: 600,
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Что попробовать дальше
Метод Phaser.Geom.Polygon.ContainsPoint — это мощный и простой инструмент для работы со сложными геометрическими областями в играх. Он снимает необходимость самостоятельно реализовывать нетривиальные алгоритмы проверки вхождения точки в многоугольник. Для экспериментов попробуйте изменить форму полигона, добавить несколько независимых полигонов с разными реакциями или использовать проверку не для курсора, а для позиции игрового спрайта, создав, например, невидимую опасную зону сложной формы.
