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

Математика столкновений — основа игровой физики. Понимание и эффективное использование геометрических проверок позволяет создавать сложное игровое взаимодействие без тяжеловесных физических движков. В этой статье мы разберем наглядный пример проверки пересечения вращающегося треугольника и двигающегося за курсором круга, используя встроенные возможности Phaser. Этот подход полезен для создания кастомных хитбоксов, триггеров событий или простых игровых механик, где важна точность и производительность.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    graphics;
    circle;
    triangle;

    create ()
    {
        this.graphics = this.add.graphics();

        this.triangle = new Phaser.Geom.Triangle.BuildEquilateral(400, 200, 200);

        this.circle = new Phaser.Geom.Circle(300, 400, 60);

        this.graphics.lineStyle(2, 0x00ff00);
        this.graphics.strokeTriangleShape(this.triangle);
        this.graphics.lineStyle(2, 0xffff00);
        this.graphics.strokeCircleShape(this.circle);

        this.input.on('pointermove', pointer =>
        {

            this.circle.x = pointer.x;
            this.circle.y = pointer.y;

        });
    }

    update ()
    {
        Phaser.Geom.Triangle.Rotate(this.triangle, 0.02);

        this.graphics.clear();
        this.graphics.lineStyle(2, 0x00ff00);
        this.graphics.strokeTriangleShape(this.triangle);

        if (Phaser.Geom.Intersects.TriangleToCircle(this.triangle, this.circle))
        {
            this.graphics.lineStyle(2, 0xff0000);
        }
        else
        {
            this.graphics.lineStyle(2, 0xffff00);
        }

        this.graphics.strokeCircleShape(this.circle);
    }
}

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

const game = new Phaser.Game(config);

Подготовка сцены и создание фигур

В методе create() инициализируется сцена. Сначала создается объект Graphics для отрисовки примитивов. Затем определяются две геометрические фигуры, с которыми мы будем работать.

this.graphics = this.add.graphics();

Треугольник создается как равносторонний с центром в точке (400, 200) и длиной стороны 200 пикселей.

this.triangle = new Phaser.Geom.Triangle.BuildEquilateral(400, 200, 200);

Круг создается с центром в (300, 400) и радиусом 60 пикселей.

this.circle = new Phaser.Geom.Circle(300, 400, 60);

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

this.input.on('pointermove', pointer => {
    this.circle.x = pointer.x;
    this.circle.y = pointer.y;
});

Анимация и проверка пересечения в Update

Вся динамика и логика столкновения происходит в методе update(), который вызывается на каждом кадре.

Сначала треугольник плавно вращается вокруг своего центра. Метод Phaser.Geom.Triangle.Rotate модифицирует исходный объект треугольника.

Phaser.Geom.Triangle.Rotate(this.triangle, 0.02);

Перед перерисовкой необходимо очистить холст Graphics от предыдущего кадра.

this.graphics.clear();

Затем треугольник отрисовывается заново с зеленым контуром.

this.graphics.lineStyle(2, 0x00ff00);
this.graphics.strokeTriangleShape(this.triangle);

Ключевой момент — проверка пересечения фигур с помощью Phaser.Geom.Intersects.TriangleToCircle. Этот метод принимает объекты треугольника и круга и возвращает true или false. В зависимости от результата выбирается цвет контура для круга: красный при пересечении, желтый — при его отсутствии.

if (Phaser.Geom.Intersects.TriangleToCircle(this.triangle, this.circle))
{
    this.graphics.lineStyle(2, 0xff0000);
}
else
{
    this.graphics.lineStyle(2, 0xffff00);
}

В конце метод отрисовывает круг с уже заданным стилем линии.

this.graphics.strokeCircleShape(this.circle);

Принцип работы и API геометрии

Phaser хранит геометрические фигуры как данные (координаты, размеры), отдельно от их графического представления. Это позволяет эффективно выполнять вычисления.

- **Объекты фигур (Phaser.Geom.Triangle, Phaser.Geom.Circle)**: Это контейнеры для математического описания. Изменение их свойств (например, circle.x) мгновенно влияет на все последующие вычисления. - **Методы модуля Intersects**: Статические функции, такие как TriangleToCircle, выполняют чисто математическую проверку пересечения, не влияя на сами объекты. Они работают быстро, так как оперируют только числами. - **Отрисовка через Graphics**: Объект this.graphics лишь визуализирует текущее состояние геометрических объектов. Важно перерисовывать его каждый кадр после изменения данных или стилей.

Такой подход — изменение данных фигуры и последующая перерисовка — является стандартным паттерном для работы с геометрией в игровом цикле.

Оптимизация и практическое применение

В примере проверка столкновения и перерисовка происходят каждый кадр. Для простых фигур это не проблема, но в проекте с сотнями объектов нужна оптимизация.

1. **Отложенные проверки**: Выполняйте Intersects только для объектов, находящихся вблизи друг друга. Используйте пространственное разбиение или предварительную проверку по ограничивающим прямоугольникам (AABB). 2. **Кеширование Graphics**: Если стили линий не меняются, можно не вызывать lineStyle каждый раз. В нашем примере стиль для круга меняется, поэтому вызов необходим. 3. **Применение в играх**: - Создание невидимых триггерных зон сложной формы. - Кастомная логика попадания снаряда (не только круглого). - Проверка, находится ли игрок в зоне видимости врага (сектор).

// Пример: предварительная грубая проверка по AABB перед точной
if (Phaser.Geom.Intersects.RectangleToCircle(triangleBounds, this.circle)) {
    // Затем выполняем точную проверку TriangleToCircle
}

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

Phaser предоставляет мощный и производительный арсенал для работы с геометрией и проверкой столкновений. Использование методов вроде TriangleToCircle позволяет легко внедрять сложные игровые механики, основанные на точной геометрии. Для экспериментов попробуйте заменить круг на прямоугольник (RectangleToTriangle), сделать проверку столкновения между несколькими движущимися треугольниками или реализовать систему предупреждения (например, мигание), когда фигуры находятся на критическом расстоянии друг от друга.