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

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

Версия 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.image('bg', 'assets/skies/darkstone.png');
        this.load.image('flare', 'assets/particles/white-flare.png');
        this.load.image('slug', 'assets/pics/card1.png');
        this.load.image('fox', 'assets/pics/card3.png');
    }

    create ()
    {
        this.add.image(400, 300, 'bg');

        const card1 = this.add.image(225, 300, 'slug').setInteractive();
        const card2 = this.add.image(575, 300, 'fox').setInteractive();

        const emitZone1 = { type: 'edge', source: card1.getBounds(), quantity: 42 };
        const emitZone2 = { type: 'edge', source: card2.getBounds(), quantity: 42 };

        const emitter = this.add.particles(0, 0, 'flare', {
            speed: 24,
            lifespan: 1500,
            quantity: 5,
            scale: { start: 0.4, end: 0 },
            advance: 2000,
            emitZone: [ emitZone1, emitZone2 ]
        });

        card1.on('pointerover', () => {

            emitter.setEmitZone(0);
            emitter.fastForward(2000);

        });

        card2.on('pointerover', () => {

            emitter.setEmitZone(1);
            emitter.fastForward(2000);

        });
    }
}

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

const game = new Phaser.Game(config);

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

В методе preload загружаются необходимые ресурсы: фон, текстура для частиц и два изображения для карточек. Ключевая работа происходит в create.

Сначала добавляется фон, затем две карточки (card1 и card2). Метод setInteractive() делает их интерактивными, то есть способными реагировать на события мыши или касания.

const card1 = this.add.image(225, 300, 'slug').setInteractive();
const card2 = this.add.image(575, 300, 'fox').setInteractive();

Создание зон испускания для эмиттера

Зона испускания (emitZone) определяет, откуда будут появляться частицы. В этом примере создаются две зоны типа 'edge'. Это означает, что частицы будут рождаться по границам прямоугольной области.

Свойство source задает эту область. Здесь используется card1.getBounds(), который возвращает прямоугольник (Phaser Geom Rectangle), описывающий границы спрайта карточки. Свойство quantity определяет количество точек на границе, из которых могут вылетать частицы — чем больше, тем равномернее распределение.

const emitZone1 = { type: 'edge', source: card1.getBounds(), quantity: 42 };
const emitZone2 = { type: 'edge', source: card2.getBounds(), quantity: 42 };

Настройка и создание эмиттера частиц

Эмиттер создается с помощью this.add.particles. Ключевой параметр здесь — emitZone. В него передается массив из двух созданных ранее зон. Это значит, что эмиттер изначально знает об обеих зонах, но по умолчанию будет использовать первую (с индексом 0).

Остальные параметры настраивают поведение самих частиц: скорость, время жизни, количество, исчезновение через уменьшение масштаба (scale). Параметр advance сразу "прокручивает" эмиттер на 2000 мс, создавая начальный всплеск частиц.

const emitter = this.add.particles(0, 0, 'flare', {
    speed: 24,
    lifespan: 1500,
    quantity: 5,
    scale: { start: 0.4, end: 0 },
    advance: 2000,
    emitZone: [ emitZone1, emitZone2 ]
});

Динамическое переключение зон по событию

Логика переключения привязана к событию 'pointerover' (наведение курсора). При наведении на card1 вызывается emitter.setEmitZone(0). Это метод эмиттера, который переключает активную зону испускания на ту, что находится в переданном ему массиве под индексом 0 (первая зона).

Вызов emitter.fastForward(2000) мгновенно генерирует порцию частиц, создавая визуальный всплеск в момент наведения, что дает четкую обратную связь игроку. Аналогичная логика работает для второй карточки.

card1.on('pointerover', () => {
    emitter.setEmitZone(0);
    emitter.fastForward(2000);
});

card2.on('pointerover', () => {
    emitter.setEmitZone(1);
    emitter.fastForward(2000);
});

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

Используя setEmitZone(), вы можете создавать сложные системы частиц, которые динамически реагируют на игровые события. Это мощный инструмент для визуализации взаимодействий. Для экспериментов попробуйте изменить тип зоны на 'random', добавить больше двух источников или привязать переключение зоны не к курсору, а к столкновениям объектов или таймеру.