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

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

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

Живой запуск

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

Исходный код


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

    create ()
    {
        const shape1 = new Phaser.Geom.Circle(0, 0, 160);
        const shape2 = new Phaser.Geom.Ellipse(0, 0, 500, 150);
        const shape3 = new Phaser.Geom.Rectangle(-150, -150, 300, 300);
        const shape4 = new Phaser.Geom.Line(-150, -150, 150, 150);
        const shape5 = new Phaser.Geom.Triangle.BuildEquilateral(0, -140, 300);
        const shapes = [ shape1, shape2, shape3, shape4, shape5 ];

        let i = 0;

        const particles = this.add.particles('flares');

        const emitter = particles.createEmitter({
            frame: { frames: [ 'red', 'green', 'blue' ], cycle: true },
            x: 400,
            y: 300,
            scale: { start: 0.5, end: 0 },
            blendMode: 'ADD',
            emitZone: { type: 'edge', source: shape1, quantity: 48, yoyo: false }
        });

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

            i++;

            if (i === shapes.length)
            {
                i = 0;
            }

            emitter.setEmitZone({ type: 'edge', source: shapes[i], quantity: 48, yoyo: false });

        });
    }
}

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

const game = new Phaser.Game(config);

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

В методе preload() мы загружаем необходимый ресурс — атлас частиц. Атлас flares содержит несколько спрайтов разного цвета (кадры) в одном изображении, что эффективно для работы с частицами.

this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.atlas('flares', 'assets/particles/flares.png', 'assets/particles/flares.json');

Создание набора геометрических фигур

В начале create() определяется массив различных геометрических объектов Phaser. Эти фигуры будут использоваться как источники (source) для зоны испускания. Каждая фигура создается с центром в точке (0, 0), что упрощает их последующее позиционирование относительно эмиттера.

const shape1 = new Phaser.Geom.Circle(0, 0, 160);
const shape2 = new Phaser.Geom.Ellipse(0, 0, 500, 150);
const shape3 = new Phaser.Geom.Rectangle(-150, -150, 300, 300);
const shape4 = new Phaser.Geom.Line(-150, -150, 150, 150);
const shape5 = new Phaser.Geom.Triangle.BuildEquilateral(0, -140, 300);
const shapes = [ shape1, shape2, shape3, shape4, shape5 ];

Инициализация менеджера частиц и эмиттера

Сначала создается менеджер частиц (ParticleManager), который отвечает за рендеринг всех связанных с ним эмиттеров. Затем в этом менеджере создается эмиттер — объект, который непосредственно испускает, обновляет и уничтожает частицы.

Ключевой параметр конфигурации — emitZone. Указав type: 'edge', мы говорим системе, что частицы должны появляться на краю фигуры-источника (source). Параметр quantity определяет, сколько точек вдоль края будет использовано для испускания частиц за один выброс.

const particles = this.add.particles('flares');

const emitter = particles.createEmitter({
    frame: { frames: [ 'red', 'green', 'blue' ], cycle: true },
    x: 400,
    y: 300,
    scale: { start: 0.5, end: 0 },
    blendMode: 'ADD',
    emitZone: { type: 'edge', source: shape1, quantity: 48, yoyo: false }
});

Динамическая смена зоны испускания

Для интерактивности добавляется обработчик клика мыши (pointerdown). При каждом клике индекс `iувеличивается, и эмиттеру назначается новая зона испускания с помощью методаsetEmitZone()`. Этот метод принимает тот же объект конфигурации, что и при создании, и мгновенно применяет изменения.

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

this.input.on('pointerdown', pointer => {
    i++;
    if (i === shapes.length) {
        i = 0;
    }
    emitter.setEmitZone({ type: 'edge', source: shapes[i], quantity: 48, yoyo: false });
});

Конфигурация игры и запуск

Стандартная конфигурация игры Phaser. Обратите внимание на type: Phaser.WEBGL — для корректной работы blend mode 'ADD' необходим WebGL-рендерер.

const config = {
    type: Phaser.WEBGL,
    width: 800,
    height: 600,
    backgroundColor: '#000',
    parent: 'phaser-example',
    scene: Example
};
const game = new Phaser.Game(config);

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

Использование EmitZone типа edge — это мощный способ добавить геометрическую точность и разнообразие в ваши системы частиц. Вы можете использовать не только примитивные фигуры, но и собственные полигоны или пути. **Идеи для экспериментов:** 1. Попробуйте анимировать саму фигуру-источник (например, вращать Phaser.Geom.Rectangle), частицы будут следовать за её контуром в реальном времени. 2. Скомбинируйте несколько эмиттеров с разными фигурами и настройками цвета, чтобы создать сложный составной эффект (например, магический круг). 3. Используйте quantity для управления плотностью частиц по краю и yoyo для изменения направления их генерации.