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

В игровой разработке часто возникает задача определения, какие объекты или точки находятся внутри заданной области. Проверять каждую точку в цикле может быть неэффективно, особенно при большом их количестве. Phaser предоставляет удобный метод `Phaser.Geom.Triangle.ContainsArray`, который позволяет за один вызов отфильтровать массив точек, находящихся внутри треугольника. Эта статья покажет, как использовать этот метод для создания визуальных эффектов и оптимизации физических или коллизионных проверок в вашей игре.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    create ()
    {
        const graphics = this.add.graphics({ fillStyle: { color: 0xaaaa00 } });

        const triangle1 = new Phaser.Geom.Triangle.BuildEquilateral(400, 25, 300);
        const triangle2 = new Phaser.Geom.Triangle.BuildEquilateral(250, 285, 300);
        const triangle3 = new Phaser.Geom.Triangle.BuildEquilateral(550, 285, 300);

        const circle = new Phaser.Geom.Circle(400, 300, 20);

        const points = [];

        while (circle.diameter < 800)
        {
            circle.getPoints(null, 15, points);

            circle.radius += 15;
        }

        const filteredPoints = [];

        Phaser.Geom.Triangle.ContainsArray(triangle1, points, false, filteredPoints);
        Phaser.Geom.Triangle.ContainsArray(triangle2, points, false, filteredPoints);
        Phaser.Geom.Triangle.ContainsArray(triangle3, points, false, filteredPoints);

        for (let i = 0; i < filteredPoints.length; i++)
        {
            graphics.fillCircle(filteredPoints[i].x, filteredPoints[i].y, 7.5);
        }
    }
}

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

const game = new Phaser.Game(config);

Подготовка геометрических фигур и точек

В примере создаются три равносторонних треугольника и один круг, который будет использоваться как источник точек. Точки генерируются по концентрическим окружностям с увеличивающимся радиусом, создавая сетку.

const graphics = this.add.graphics({ fillStyle: { color: 0xaaaa00 } });

const triangle1 = new Phaser.Geom.Triangle.BuildEquilateral(400, 25, 300);
const triangle2 = new Phaser.Geom.Triangle.BuildEquilateral(250, 285, 300);
const triangle3 = new Phaser.Geom.Triangle.BuildEquilateral(550, 285, 300);

const circle = new Phaser.Geom.Circle(400, 300, 20);
const points = [];

while (circle.diameter < 800)
{
    circle.getPoints(null, 15, points);
    circle.radius += 15;
}

Метод Phaser.Geom.Triangle.BuildEquilateral создаёт равносторонний треугольник по координатам верхней вершины и длине стороны. Цикл while заполняет массив points, получая точки на каждой окружности с помощью circle.getPoints. Второй аргумент 15 указывает шаг угла в градусах между точками. Третий аргумент points — массив для накопления результатов.

Фильтрация точек методом ContainsArray

Ключевой метод Phaser.Geom.Triangle.ContainsArray проверяет, какие точки из исходного массива находятся внутри заданного треугольника, и добавляет их в результирующий массив.

const filteredPoints = [];

Phaser.Geom.Triangle.ContainsArray(triangle1, points, false, filteredPoints);
Phaser.Geom.Triangle.ContainsArray(triangle2, points, false, filteredPoints);
Phaser.Geom.Triangle.ContainsArray(triangle3, points, false, filteredPoints);

Параметры метода: 1. triangle1, triangle2, triangle3 — объекты треугольников, которые выступают в роли фильтрующих областей. 2. points — исходный массив точек для проверки. 3. false — флаг, указывающий, нужно ли очищать результирующий массив filteredPoints перед добавлением новых точек. Значение false позволяет накапливать точки из нескольких треугольников в одном массиве. 4. filteredPoints — массив, в который будут добавлены точки, прошедшие проверку.

Этот подход гораздо эффективнее, чем ручной перебор точек в цикле с вызовом Phaser.Geom.Triangle.Contains для каждой.

Визуализация результата

После фильтрации мы визуализируем отобранные точки с помощью графического объекта.

for (let i = 0; i < filteredPoints.length; i++)
{
    graphics.fillCircle(filteredPoints[i].x, filteredPoints[i].y, 7.5);
}

Цикл проходит по массиву filteredPoints и для каждой точки рисует закрашенный круг с помощью метода graphics.fillCircle. В результате на экране отобразятся только те точки, которые находятся внутри одного из трёх треугольников, создавая интересный паттерн.

Практическое применение в играх

Метод ContainsArray полезен не только для визуализации. Рассмотрим сценарии его применения:

* **Зоны поражения сложной формы:** Если область заклинания или взрыва задана треугольником (или составлена из нескольких треугольников), можно быстро найти всех врагов (их точки позиционирования), попавших в зону. * **Генерация контента внутри полигона:** Можно сгенерировать массив потенциальных позиций для размещения предметов, а затем отфильтровать только те, что находятся внутри безопасной или игровой зоны, заданной треугольниками. * **Оптимизация:** Использование этого встроенного метода, как правило, эффективнее самописных циклов, так как он реализован на уровне движка.

// Пример: проверка попадания группы пуль в треугольную зону
const bullets = this.bullets.getChildren();
const bulletPositions = bullets.map(bullet => ({ x: bullet.x, y: bullet.y }));
const hits = [];

Phaser.Geom.Triangle.ContainsArray(dangerZoneTriangle, bulletPositions, true, hits);
// hits теперь содержит точки пуль внутри dangerZoneTriangle

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

Метод Phaser.Geom.Triangle.ContainsArray — это мощный и эффективный инструмент для работы с геометрией в Phaser. Он позволяет за один вызов решить задачу пространственной фильтрации массива точек. Для экспериментов попробуйте изменить форму зон, используя другие методы из Phaser.Geom (например, Polygon или Rectangle), или примените фильтрацию для динамически меняющихся областей, например, следящих за курсором мыши. Это может стать основой для систем выделения объектов, областей магии или процедурного размещения элементов на уровне.