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

Столкновения — основа игровой механики. Проверка пересечения объектов часто оказывается дорогой операцией для процессора, особенно когда объектов много. Phaser предоставляет оптимизированные методы для работы с геометрией, такие как `Phaser.Geom.Rectangle.Overlaps`. В этой статье разберем, как эффективно проверять пересечение прямоугольников, визуализировать результат и применять это в своих играх.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    a = 0;
    graphics;
    rectangles;
    rect;

    create ()
    {
        this.graphics = this.add.graphics({ lineStyle: { width: 2, color: 0x0000aa }, fillStyle: { color: 0xaa0000 }});

        this.rect = new Phaser.Geom.Rectangle(0, 0, 30, 30);

        this.rectangles = [];

        for (let x = 0; x < 10; x++)
        {
            for (let y = 0; y < 10; y++)
            {
                this.rectangles.push(new Phaser.Geom.Rectangle(x * 80, y * 60, 80, 60));
            }
        }
    }

    update ()
    {
        this.a += 0.005;

        if (this.a > Math.PI * 2)
        {
            this.a -= Math.PI * 2;
        }

        this.rect.x = 370 - Math.cos(this.a) * 370;
        this.rect.y = 270 - Math.sin(this.a * 2) * 270;

        this.graphics.clear();
        this.graphics.fillRectShape(this.rect);

        // stroke blue all rectangles NOT overlapping rect
        this.graphics.lineStyle(1, 0x0000aa);

        for (let i = 0; i < this.rectangles.length; i++)
        {
            if (!Phaser.Geom.Rectangle.Overlaps(this.rectangles[i], this.rect))
            {
                this.graphics.strokeRectShape(this.rectangles[i]);
            }
        }

        // stroke red all rectangles that DO overlap rect
        this.graphics.lineStyle(2, 0xaa0000);

        for (let i = 0; i < this.rectangles.length; i++)
        {
            if (Phaser.Geom.Rectangle.Overlaps(this.rectangles[i], this.rect))
            {
                this.graphics.strokeRectShape(this.rectangles[i]);
            }
        }
    }
}

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

const game = new Phaser.Game(config);

Подготовка сцены и объектов

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

graphics = this.add.graphics({ lineStyle: { width: 2, color: 0x0000aa }, fillStyle: { color: 0xaa0000 }});
rect = new Phaser.Geom.Rectangle(0, 0, 30, 30);
rectangles = [];
for (let x = 0; x < 10; x++) {
    for (let y = 0; y < 10; y++) {
        rectangles.push(new Phaser.Geom.Rectangle(x * 80, y * 60, 80, 60));
    }
}

Анимация и движение основного прямоугольника

В методе update() реализована анимация движения небольшого прямоугольника rect по сложной траектории. Его позиция пересчитывается каждый кадр на основе синуса и косинуса от угла `a`, который плавно увеличивается.

a += 0.005;
if (a > Math.PI * 2) {
    a -= Math.PI * 2;
}
rect.x = 370 - Math.cos(a) * 370;
rect.y = 270 - Math.sin(a * 2) * 270;

После обновления позиции, перед отрисовкой столкновений, холст графики очищается вызовом this.graphics.clear(), и движущийся прямоугольник заливается красным цветом.

graphics.clear();
graphics.fillRectShape(rect);

Принцип работы метода Overlaps

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

В коде примера этот метод используется дважды в циклах для сравнения движущегося прямоугольника rect с каждым из ста статичных.

Визуализация непересекающихся прямоугольников

Сначала все прямоугольники, которые НЕ пересекаются с движущимся rect, обводятся тонкой синей линией. Для этого устанавливается стиль линии и в цикле выполняется проверка с помощью оператора отрицания `!перед вызовомOverlaps`.

graphics.lineStyle(1, 0x0000aa);
for (let i = 0; i < rectangles.length; i++) {
    if (!Phaser.Geom.Rectangle.Overlaps(rectangles[i], rect)) {
        graphics.strokeRectShape(rectangles[i]);
    }
}

Визуализация пересекающихся прямоугольников

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

graphics.lineStyle(2, 0xaa0000);
for (let i = 0; i < rectangles.length; i++) {
    if (Phaser.Geom.Rectangle.Overlaps(rectangles[i], rect)) {
        graphics.strokeRectShape(rectangles[i]);
    }
}

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

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

Метод Phaser.Geom.Rectangle.Overlaps — это простой и эффективный инструмент для проверки пересечений прямоугольных областей. Он идеально подходит для создания хитбоксов, зон активации или простых коллизий в 2D-играх. **Идеи для экспериментов:** 1. Замените сетку прямоугольников на случайно расположенные объекты разного размера. 2. Сделайте так, чтобы при пересечении статичный прямоугольник менял цвет или исчезал. 3. Используйте проверку Overlaps не для графики, а для игровой логики, например, для начисления очков при прохождении через ворота. 4. Попробуйте оптимизировать проверку, предварительно отфильтровав объекты по расстоянию, чтобы не проверять те, которые заведомо далеко.