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

При создании визуальных эффектов в играх часто требуется выпустить не бесконечный поток частиц, а строго определённое количество. Например, для эффекта попадания, взрыва маленького объекта или магического заклинания с ограниченным действием. Phaser предоставляет для этого простую и мощную настройку `stopAfter` в конфигурации эмиттера частиц. В этой статье мы разберём, как работает параметр `stopAfter`, чем он отличается от ручного управления эмиттером и в каких игровых ситуациях его применение делает код чище и эффективнее.

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

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

        const emitter = this.add.particles(0, 0, 'match3', {
            frame: 'Match3_Icon_28',
            x: { start: 700, end: -64, ease: 'sine.in' },
            y: { start: 600, end: -64 },
            lifespan: 1500,
            frequency: 150,
            emitting: false,
            stopAfter: 6, //  This emitter will release 6 particles and then stop
            scale: 0.5
        });

        this.add.text(400, 32, 'Click to release 6 particles', { font: '22px Arial', fill: '#ffffff' }).setOrigin(0.5);

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

            emitter.start();

        });
    }
}

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

const game = new Phaser.Game(config);

Что делает stopAfter?

Параметр stopAfter (остановить после) — это свойство конфигурации эмиттера частиц в Phaser. Когда вы устанавливаете ему числовое значение, эмиттер автоматически прекратит испускать новые частицы после того, как выпустит указанное количество.

Это поведение отличается от простого выключения эмиттера (emitting: false). При emitting: false эмиттер изначально неактивен. stopAfter же позволяет запустить эмиттер (например, по клику), дать ему выпустить фиксированную «порцию» частиц и затем автоматически остановиться, готовый к следующему запуску.

stopAfter: 6 // Этот эмиттер выпустит 6 частиц и затем остановится

Разбор примера кода: эмиттер с ограниченным выпуском

Рассмотрим ключевые части предоставленного примера. В методе create() создаётся эмиттер частиц. Обратите внимание на два важных параметра в его конфигурационном объекте.

Параметр emitting: false устанавливает, что при создании эмиттер неактивен. Он будет ждать команды на старт.

Параметр stopAfter: 6 — это наша основная настройка. Она инструктирует эмиттер: «Когда тебя запустят, выпусти ровно 6 частиц и затем перестань испускать новые».

const emitter = this.add.particles(0, 0, 'match3', {
    frame: 'Match3_Icon_28',
    x: { start: 700, end: -64, ease: 'sine.in' },
    y: { start: 600, end: -64 },
    lifespan: 1500,
    frequency: 150,
    emitting: false, // Изначально выключен
    stopAfter: 6,    // Выпустит 6 частиц и остановится
    scale: 0.5
});

Запуск эмиттера привязан к событию клика (pointerdown). Каждый клик вызывает emitter.start(), который активирует эмиттер, тот выпускает 6 частиц и снова «засыпает».

this.input.on('pointerdown', () => {
    emitter.start(); // Запускаем эмиттер. Он сам остановится после 6 частиц.
});

Практическое применение: от взрывов до UI-эффектов

Использование stopAfter идеально подходит для сценарных, непостоянных эффектов.

1. **Эффекты попадания:** При попадании пули или стрелы можно выпустить небольшую порцию частиц (искры, брызги), которая точно соответствует одному событию. 2. **Коллекционные предметы:** Когда игрок подбирает монету или кристалл, эффект исчезновения/поглощения предмета может быть реализован как выпуск 5-10 частиц, имитирующих сияние. 3. **Ограниченная магия:** Заклинание, которое наносит три волны урона, может визуализироваться тремя порциями частиц. 4. **UI-обратная связь:** Анимация нажатия кнопки, где выпускается несколько частиц от точки клика.

Преимущество перед ручным управлением (например, использованием таймера и вызовом emitter.stop()) — в простоте и надёжности. Phaser сам считает частицы и вовремя останавливает эмиттер, вам не нужно заводить дополнительные переменные-счётчики или отслеживать время.

Важные нюансы и взаимодействие с другими параметрами

Поведение stopAfter тесно связано с другими параметрами эмиттера, такими как frequency (частота) и lifespan (время жизни частицы).

- **frequency:** Определяет задержку (в миллисекундах) между выпуском отдельных частиц. При frequency: 150 и stopAfter: 6 эмиттер будет активен примерно 900 мс (6 * 150), пока выпускает все частицы. Это не мгновенный взрыв, а растянутый во времени эффект. - **lifespan:** Время жизни каждой выпущенной частицы. Эмиттер остановит выпуск новых через 900 мс (в нашем примере), но уже выпущенные 6 частиц продолжат жить и анимироваться ещё 1500 мс каждая. - **Повторный запуск:** После срабатывания stopAfter эмиттер переходит в состояние emitting: false. Последующий вызов emitter.start() снова активирует его, и он опять выпустит ровно stopAfter частиц. Это цикличное поведение.

// Эмиттер с быстрой частотой создаст «порцию» частиц почти мгновенно
frequency: 50,
stopAfter: 10,
// Эмиттер с медленной частотой будет создавать эффект капель
frequency: 500,
stopAfter: 4

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

Параметр stopAfter — это элегантный и производительный способ создать одноразовые или порционные эффекты частиц в Phaser. Он избавляет разработчика от необходимости вручную управлять таймерами и счётчиками, делая код более декларативным и простым для чтения. **Идеи для экспериментов:** 1. Свяжите stopAfter с силой удара: при сильном ударе выпускайте 15 частиц, при слабом — 5. 2. Создайте цепочку эмиттеров с разным stopAfter, которые запускаются друг за другом для сложного многоэтапного эффекта. 3. Поэкспериментируйте, что будет, если изменить stopAfter динамически, уже после создания эмиттера, через emitter.setStopAfter(count).