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

При создании спецэффектов, таких как дым, огонь или волшебные частицы, важно понимать, где они находятся на экране. Метод `getBounds()` у эмиттера частиц позволяет получить его текущие границы, что полезно для оптимизации (например, отключения рендеринга за пределами камеры), обнаружения столкновений с частицами или визуальной отладки. В этой статье разберем, как получить и использовать эти границы на практике.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.atlas('bubbles', 'assets/particles/bubbles.png', 'assets/particles/bubbles.json');
    }

    create ()
    {
        this.emitter = this.add.particles(400, 400, 'bubbles', {
            frame: [ 'bluebubble', 'redbubble', 'greenbubble', 'silverbubble' ],
            scale: { min: 0.1, max: 0.5 },
            speed: { min: 20, max: 40 },
            alpha: { start: 1, end: 0 },
            lifespan: 2000,
            frequency: 50,
            gravityY: -90,
            particleBringToTop: false
        });

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

        this.bounds = this.emitter.getBounds(10, 6000);
    }

    update ()
    {
        this.g.clear();
        this.g.lineStyle(1, 0x00ff00);

        //  The current emitter bounds
        const vb = this.emitter.getBounds();

        this.g.strokeRectShape(vb);

        if (Phaser.Geom.Rectangle.ContainsRect(this.bounds, vb))
        {
            this.g.lineStyle(1, 0xffff00);
        }
        else
        {
            this.g.lineStyle(1, 0xff0000);
        }

        //  The pre-calculated emitter bounds
        this.g.strokeRectShape(this.bounds);
    }
}

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

const game = new Phaser.Game(config);

Создание эмиттера частиц

В начале сцены в методе preload() загружается атлас с текстурами для частиц. В методе create() создается сам эмиттер с помощью this.add.particles(). Конфигурация определяет поведение: размер, скорость, продолжительность жизни и гравитацию.

this.emitter = this.add.particles(400, 400, 'bubbles', {
    frame: [ 'bluebubble', 'redbubble', 'greenbubble', 'silverbubble' ],
    scale: { min: 0.1, max: 0.5 },
    speed: { min: 20, max: 40 },
    alpha: { start: 1, end: 0 },
    lifespan: 2000,
    frequency: 50,
    gravityY: -90,
    particleBringToTop: false
});

Также создается объект Graphics для рисования и предварительно вычисляются границы эмиттера на 6000 частиц с помощью getBounds(10, 6000). Второй параметр — это примерное количество частиц для расчета.

this.g = this.add.graphics();
this.bounds = this.emitter.getBounds(10, 6000);

Получение текущих границ

В методе update() каждый кадр получаются актуальные границы эмиттера, которые могут меняться из-за движения частиц. Метод getBounds() без параметров возвращает прямоугольник (Phaser.Geom.Rectangle), охватывающий все живые частицы.

const vb = this.emitter.getBounds();

Эти границы рисуются зеленой линией с помощью strokeRectShape(). Это позволяет визуализировать, где именно находятся частицы в реальном времени.

this.g.clear();
this.g.lineStyle(1, 0x00ff00);
this.g.strokeRectShape(vb);

Сравнение границ для визуализации

Чтобы отслеживать, не вышли ли частицы за пределы ожидаемой области, используется сравнение прямоугольников. Phaser.Geom.Rectangle.ContainsRect() проверяет, содержится ли текущая граница (vb) внутри предварительно рассчитанной (this.bounds).

if (Phaser.Geom.Rectangle.ContainsRect(this.bounds, vb))
{
    this.g.lineStyle(1, 0xffff00);
}
else
{
    this.g.lineStyle(1, 0xff0000);
}

Если границы не выходят, предрасчитанная область рисуется желтым, а если выходят — красным. Это наглядный способ отладки: красный цвет сигнализирует о том, что частицы ведут себя не так, как предполагалось (например, из-за гравитации или скорости).

this.g.strokeRectShape(this.bounds);

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

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

* **Оптимизация производительности:** Если границы эмиттера полностью вышли за пределы видимой камеры (this.cameras.main), можно временно отключить его рендеринг или генерацию частиц. * **Взаимодействие с игроком:** Проверить, пересекаются ли границы эмиттера с хитбоксом персонажа, чтобы наносить урон от огня или давать баффы от магических аур. * **Динамическое управление:** На основе границ можно автоматически перемещать эмиттер или менять его параметры, чтобы эффект оставался в нужной зоне.

// Пример: проверка видимости эмиттера
const cameraBounds = this.cameras.main.worldView;
if (!Phaser.Geom.Rectangle.Overlaps(cameraBounds, vb))
{
    this.emitter.setVisible(false); // Скрыть, если не видно
}

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

Метод getBounds() — мощный инструмент для работы с эмиттерами частиц в Phaser. Он открывает возможности для оптимизации, отладки и создания интерактивных эффектов. Попробуйте экспериментировать: например, привяжите границы эмиттера к движущемуся спрайту или создайте систему, где частицы уничтожаются при выходе за определенную зону, используя Rectangle.Contains() для каждой частицы.