О чем этот пример
Определение, находится ли точка внутри сложной фигуры, — фундаментальная задача для игровой логики. Это нужно для триггеров зон, попаданий в хитбоксы неправильной формы или для создания областей на карте. В примере из официальной документации 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 для создания «острова» с проходимыми участками.
