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

При создании визуальных эффектов, таких как частицы, часто требуется проверять их взаимодействие с другими объектами игры или отлаживать их поведение. Например, для определения столкновений или точного позиционирования. В этой статье мы разберем, как в Phaser получить границы (bounding box) каждой активной частицы и визуализировать их для отладки. Это полезно не только для отладки, но и для реализации нестандартной логики, связанной с областью влияния частиц.

Версия 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('jellies', 'assets/atlas/jellies.png', 'assets/atlas/jellies.json');
    }

    create ()
    {
        this.emitter = this.add.particles(0, 0, 'jellies', {
            frame: [ 'WithShadow/Jelly5', 'WithShadow/Jelly2', 'WithShadow/Jelly4' ],
            scale: { start: 1, end: 0 },
            rotate: { start: 0, end: 360 },
            speed: { min: 200, max: 250 },
            lifespan: 2000,
            emitting: false
        });

        this.input.on('pointerdown', () =>
        {
            this.emitter.emitParticleAt(400, 300);
        });

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

        this.add.text(16, 16, 'Click to release a particle');
    }

    update ()
    {
        this.graphics.clear();

        this.graphics.lineStyle(1, 0x00ff00);

        this.emitter.forEachAlive(particle => {

            this.graphics.strokeRectShape(particle.getBounds());

        });
    }
}

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

const game = new Phaser.Game(config);

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

В методе create() сцены создается источник частиц (ParticleEmitter). Он настраивается на использование атласа jellies и будет испускать частицы с одним из трех кадров (frame). Эмиттер изначально не активен (emitting: false), а частицы будут выпускаться по щелчку мыши.

Частицы получают анимацию изменения масштаба (scale), вращения (rotate) и случайную скорость (speed). Их время жизни (lifespan) установлено в 2000 миллисекунд.

this.emitter = this.add.particles(0, 0, 'jellies', {
    frame: [ 'WithShadow/Jelly5', 'WithShadow/Jelly2', 'WithShadow/Jelly4' ],
    scale: { start: 1, end: 0 },
    rotate: { start: 0, end: 360 },
    speed: { min: 200, max: 250 },
    lifespan: 2000,
    emitting: false
});

По клику мыши в позицию (400, 300) испускается одна частица с помощью метода emitParticleAt().

this.input.on('pointerdown', () => {
    this.emitter.emitParticleAt(400, 300);
});

Визуализация границ с помощью Graphics

Для отрисовки контуров (границ) частиц создается объект Graphics (this.graphics). Этот объект будет перерисовываться каждый кадр в методе update().

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

В update() сначала очищается предыдущее содержимое Graphics, затем устанавливается стиль линии толщиной 1 пиксель зеленого цвета.

this.graphics.clear();
this.graphics.lineStyle(1, 0x00ff00);

Итерация по частицам и получение их границ

Ключевой метод для работы с активными частицами — forEachAlive(). Он перебирает все частицы эмиттера, у которых еще не истек срок жизни (lifespan).

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

this.emitter.forEachAlive(particle => {
    this.graphics.strokeRectShape(particle.getBounds());
});

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

Конфигурация игры и практическое применение

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

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

Получение границ частицы через getBounds() — это мощный инструмент отладки. Вы можете использовать возвращаемый объект Rectangle не только для рисования, но и для проверки пересечений с другими прямоугольниками (Phaser.Geom.Intersects.RectangleToRectangle), что открывает возможности для кастомной логики столкновений частиц с игровым миром.

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

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