О чем этот пример
В игровой разработке часто возникает задача определить область, охватывающую набор объектов или точек. Например, для создания камеры, следящей за группой юнитов, или для проверки групповых столкновений. Вручную рассчитывать такой прямоугольник — утомительно. К счастью, Phaser предоставляет для этого удобный математический метод. В этой статье мы разберем пример использования `Phaser.Math.GetVec2Bounds`, который автоматически вычисляет минимальный прямоугольник (AABB — axis-aligned bounding box), содержащий все заданные векторы. Мы увидим, как это работает интерактивно, и как можно применить этот подход в реальных игровых механиках.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
create ()
{
const graphics = this.add.graphics({ lineStyle: { width: 2, color: 0x2266aa }, fillStyle: { color: 0x2266aa } });
const points = [
new Phaser.Math.Vector2(Math.random() * 400 + 200, Math.random() * 300 + 150),
new Phaser.Math.Vector2(Math.random() * 400 + 200, Math.random() * 300 + 150),
new Phaser.Math.Vector2(Math.random() * 400 + 200, Math.random() * 300 + 150)
];
this.input.on('pointermove', pointer =>
{
points[0].copy(pointer);
redraw();
});
this.input.on('pointerdown', pointer =>
{
points.push(new Phaser.Math.Vector2(pointer.x, pointer.y));
redraw();
});
redraw();
function redraw ()
{
graphics.clear();
const rect = Phaser.Math.GetVec2Bounds(points);
graphics.strokeRectShape(rect);
for (let i = 0; i < points.length; i++)
{
graphics.fillPointShape(points[i], 5);
}
}
}
}
const config = {
width: 800,
height: 600,
type: Phaser.AUTO,
parent: 'phaser-example',
scene: Example
};
const game = new Phaser.Game(config);
Суть метода GetVec2Bounds
Метод Phaser.Math.GetVec2Bounds принимает массив объектов типа Phaser.Math.Vector2 и возвращает объект типа Phaser.Geom.Rectangle. Этот прямоугольник является выровненным по осям (его стороны параллельны осям X и Y) и имеет минимально возможные размеры, чтобы вместить все переданные точки.
Это крайне полезно для быстрого определения границ любого набора позиций в игровом мире. Внутри метода происходит поиск минимальных и максимальных значений по осям X и Y среди всех векторов.
Разбор кода: создание и управление точками
В примере создается массив points, изначально содержащий три случайные точки. Затем на события указателя мыши добавляется интерактивность.
При движении указателя (pointermove) первая точка массива перезаписывается текущими координатами курсора с помощью метода copy().
При клике (pointerdown) в массив добавляется новая точка в позиции клика.
После каждого изменения вызывается функция redraw(), которая перерисовывает сцену.
const points = [
new Phaser.Math.Vector2(Math.random() * 400 + 200, Math.random() * 300 + 150),
new Phaser.Math.Vector2(Math.random() * 400 + 200, Math.random() * 300 + 150),
new Phaser.Math.Vector2(Math.random() * 400 + 200, Math.random() * 300 + 150)
];
this.input.on('pointermove', pointer => {
points[0].copy(pointer);
redraw();
});
this.input.on('pointerdown', pointer => {
points.push(new Phaser.Math.Vector2(pointer.x, pointer.y));
redraw();
});
Вычисление прямоугольника и отрисовка
Ядро логики находится в функции redraw(). Сначала очищается графика graphics.clear(). Затем для текущего массива точек вычисляется ограничивающий прямоугольник.
Полученный объект rect передается в метод graphics.strokeRectShape() для отрисовки его контура.
После этого в цикле отрисовываются сами точки с помощью graphics.fillPointShape(), чтобы визуализировать исходные данные.
function redraw ()
{
graphics.clear();
const rect = Phaser.Math.GetVec2Bounds(points);
graphics.strokeRectShape(rect);
for (let i = 0; i < points.length; i++)
{
graphics.fillPointShape(points[i], 5);
}
}
Практическое применение в играх
1. **Интеллектуальная камера:** Рассчитав прямоугольник, охватывающий всех игроков в кооперативной игре или ключевые объекты уровня, вы легко можете задать положение и масштаб камеры (this.cameras.main), чтобы все поместилось в поле зрения.
2. **Оптимизация физики:** Вместо проверки столкновений каждого объекта в группе с целью, можно сначала проверить столкновение ограничивающих прямоугольников групп. Если прямоугольники не пересекаются, то и ни один объект из групп не сталкивается, что экономит ресурсы.
3. **Выделение области (Drag-select):** В стратегиях, когда игрок выделяет юнитов рамкой, полученные координаты углов рамки можно использовать для быстрого проверки, какие юниты (их точки позиции) находятся внутри рассчитанного прямоугольника.
Метод работает именно с векторами (Vector2), поэтому если у вас есть спрайты, сначала получите их мировые координаты через sprite.x и sprite.y.
Что попробовать дальше
Метод Phaser.Math.GetVec2Bounds — это небольшой, но мощный инструмент для работы с пространственными данными в Phaser. Он избавляет от необходимости писать циклы для поиска min/max значений вручную.
**Для экспериментов попробуйте:**
- Связать точки не случайным образом, а позициями спрайтов из группы (Phaser.GameObjects.Group).
- Использовать вычисленный прямоугольник для динамического изменения зума камеры так, чтобы он всегда вмещал все точки с небольшим отступом.
- Реализовать систему подсветки области при наведении, используя прямоугольник от GetVec2Bounds как основу для графики.
