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

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

Версия 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, 180);
        const shape2 = new Phaser.Geom.Circle(0, 0, 200);

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

        particles.createEmitter({
            frame: 'red',
            x: 400, y: 300,
            lifespan: 2000,
            quantity: 4,
            scale: 0.2,
            alpha: { start: 1, end: 0 },
            blendMode: 'ADD',
            emitZone: { type: 'random', source: shape1 }
        });

        particles.createEmitter({
            frame: 'yellow',
            x: 400, y: 300,
            speed: 0,
            lifespan: 1000,
            quantity: 1,
            scale: { start: 0.4, end: 0 },
            blendMode: 'ADD',
            emitZone: { type: 'edge', source: shape2, 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);

Загрузка атласа частиц

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

В методе preload мы используем this.load.atlas(). Первый аргумент — ключ ресурса ('flares'), по которому мы будем к нему обращаться. Второй и третий — пути к изображению и JSON-файлу с данными о расположении фреймов.

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

Создание фабрики частиц и эмиттеров

Эмиттер нельзя создать просто в сцене. Сначала нужно создать фабрику (менеджер) частиц с помощью this.add.particles(), передав ключ загруженного атласа. Эта фабрика будет управлять всеми эмиттерами, которые мы к ней добавим.

Затем мы вызываем метод .createEmitter() у фабрики, передавая конфигурационный объект. В этом объекте определяются все свойства частиц: фрейм для текстуры, начальные координаты (`x,y), время жизни (lifespan), количество частиц, выпускаемых за один цикл (quantity`), и многое другое.

const particles = this.add.particles('flares');
particles.createEmitter({
    frame: 'red',
    x: 400, y: 300,
    lifespan: 2000,
    quantity: 4,
    scale: 0.2,
    alpha: { start: 1, end: 0 },
    blendMode: 'ADD',
    emitZone: { type: 'random', source: shape1 }
});

Зона эмиттера типа 'random'

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

Мы создаем объект Phaser.Geom.Circle — круг с центром в точке (0, 0) и радиусом 180 пикселей. Обратите внимание: координаты эмиттера заданы отдельно (x: 400, y: 300), а центр круга находится в (0,0) относительно этой точки. Поэтому частицы будут появляться в случайном месте внутри круга радиусом 180 пикселей вокруг центра (400, 300).

const shape1 = new Phaser.Geom.Circle(0, 0, 180);
// ... внутри createEmitter:
emitZone: { type: 'random', source: shape1 }

Зона эмиттера типа 'edge'

Второй тип зоны — 'edge'. В отличие от 'random', частицы будут появляться строго **на границе** фигуры. Это идеально для создания контурных эффектов, например, силового поля или кольца.

Мы создаем второй круг с радиусом 200. В конфигурации зоны появляются новые параметры: quantity определяет, сколько точек на границе фигуры будет использоваться для расчета позиций (чем больше, тем равномернее распределение). Параметр yoyo в данном контексте не применяется для зон типа 'edge' и может быть опущен.

const shape2 = new Phaser.Geom.Circle(0, 0, 200);
// ... внутри createEmitter:
emitZone: { type: 'edge', source: shape2, quantity: 48, yoyo: false }

Настройка поведения частиц

Помимо зоны эмиттера, конфигурация позволяет тонко настраивать поведение и внешний вид каждой частицы. Сравним два эмиттера из примера:

*   **Красные частицы (`frame: 'red'`)**: Появляются внутри круга (`random`), живут 2 секунды (`lifespan: 2000`), затухают от непрозрачных к прозрачным (`alpha: { start: 1, end: 0 }`).
*   **Желтые частицы (`frame: 'yellow'`)**: Появляются на границе круга (`edge`), не имеют начальной скорости (`speed: 0`), живут 1 секунду и масштабируются от размера 0.4 до 0 (`scale: { start: 0.4, end: 0 }`), создавая эффект "схлопывания".
// Свойства для эффекта схлопывания
speed: 0,
lifespan: 1000,
scale: { start: 0.4, end: 0 },

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

Использование emitZone с геометрическими фигурами открывает огромный простор для создания сложных визуальных эффектов без необходимости вручную рассчитывать координаты. Вы можете экспериментировать: замените Phaser.Geom.Circle на Phaser.Geom.Rectangle, Phaser.Geom.Line или Phaser.Geom.Triangle. Попробуйте анимировать саму фигуру-источник (меняя её радиус или координаты) во время работы эмиттера — это создаст динамические эффекты расходящихся волн или пульсирующих облаков. Комбинируя несколько эмиттеров с разными зонами и настройками, вы сможете собрать практически любой необходимый эффект.