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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    image;

    preload ()
    {
        
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('phaser-logo', 'assets/sprites/phaser3-logo-x2.png');
    }

    create ()
    {
        const image = this.add.image(640, 360, 'phaser-logo');

        const text = this.add.text(640, 500, 'Bloom with Parallel Filters', { font: '32px Arial', fill: '#88ffff' }).setOrigin(0.5);

        const parallelFilters = this.cameras.main.filters.internal.addParallelFilters();
        parallelFilters.top.addThreshold(0.5, 1);
        parallelFilters.top.addBlur();
        parallelFilters.blend.blendMode = Phaser.BlendModes.ADD;
        parallelFilters.blend.amount = 0;

        // Alternatively, instead of creating parallelFilters,
        // use Actions.AddEffectBloom:
        // const { parallelFilters } = Phaser.Actions.AddEffectBloom(this.cameras.main, { blendAmount: 0 });

        this.tweens.add({
            targets: parallelFilters.blend,
            amount: 2,
            duration: 1000,
            yoyo: true,
            repeat: -1,
            ease: 'Sine.easeInOut'
        });
    }
}

const config = {
    type: Phaser.WEBGL,
    width: 1280,
    height: 720,
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);

Настройка сцены и загрузка ассетов

Всё начинается с базовой структуры класса сцены Example. В методе preload() мы устанавливаем базовый URL для загрузки и загружаем изображение логотипа Phaser. Это стандартный подход для подготовки ресурсов.

class Example extends Phaser.Scene
{
    image;

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('phaser-logo', 'assets/sprites/phaser3-logo-x2.png');
    }

Создание объектов и параллельных фильтров

В методе create() мы размещаем изображение и текст на сцене. Затем происходит самое интересное — создание группы параллельных фильтров для основной камеры. Мы получаем доступ к внутренней системе фильтров камеры через this.cameras.main.filters.internal и вызываем метод addParallelFilters(). Этот метод возвращает объект, который позволяет работать с несколькими графическими эффектами одновременно.

create ()
{
    const image = this.add.image(640, 360, 'phaser-logo');
    const text = this.add.text(640, 500, 'Bloom with Parallel Filters', { font: '32px Arial', fill: '#88ffff' }).setOrigin(0.5);

    const parallelFilters = this.cameras.main.filters.internal.addParallelFilters();

Далее мы настраиваем полученный объект parallelFilters. У него есть свойство top, представляющее собой цепочку фильтров. Мы добавляем в неё два фильтра: сначала addThreshold(0.5, 1), который отсекает все пиксели с яркостью ниже 0.5 (оставляя только яркие участки), а затем addBlur(), который размывает полученную маску. Свойство blend отвечает за смешивание результата с исходным изображением. Мы устанавливаем ему режим наложения Phaser.BlendModes.ADD (сложение цветов) и начальную интенсивность (amount) в 0.

parallelFilters.top.addThreshold(0.5, 1);
    parallelFilters.top.addBlur();
    parallelFilters.blend.blendMode = Phaser.BlendModes.ADD;
    parallelFilters.blend.amount = 0;

Альтернативный способ: использование Actions.AddEffectBloom

Phaser предоставляет упрощённый способ создания этого же эффекта через Phaser.Actions.AddEffectBloom. Этот статический метод делает всю ручную настройку за вас. В примере он закомментирован, но вы можете использовать его для быстрого прототипирования.

// Alternatively, instead of creating parallelFilters,
    // use Actions.AddEffectBloom:
    // const { parallelFilters } = Phaser.Actions.AddEffectBloom(this.cameras.main, { blendAmount: 0 });

Метод принимает камеру и объект с настройками (например, blendAmount) и возвращает тот же объект parallelFilters, что и при ручном создании. Это удобно, когда нужен стандартный bloom без тонкой настройки каждого этапа.

Анимация эффекта с помощью твинов

Чтобы эффект не был статичным, мы анимируем интенсивность наложения (смешивания) с помощью системы твинов Phaser. Мы создаём твин, который нацелен на свойство amount объекта parallelFilters.blend.

this.tweens.add({
        targets: parallelFilters.blend,
        amount: 2,
        duration: 1000,
        yoyo: true,
        repeat: -1,
        ease: 'Sine.easeInOut'
    });

Параметры твина: - targets: объект, чьё свойство будет анимировано. - amount: целевое значение интенсивности (от 0 до 2). - duration: длительность анимации в миллисекундах (1000 мс = 1 секунда). - yoyo: если true, анимация будет проигрываться в обратном порядке после завершения. - repeat: -1: бесконечное повторение анимации. - ease: функция плавности 'Sine.easeInOut' для создания мягкого ускорения и замедления.

В результате интенсивность свечения плавно нарастает от 0 до 2 и обратно, создавая пульсирующий эффект.

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

В конце кода определяется стандартная конфигурация игры config. Обратите внимание, что для работы фильтров необходим рендерер Phaser.WEBGL. Мы указываем размеры холста, ID родительского элемента и класс сцены. Затем создаётся экземпляр игры new Phaser.Game(config).

const config = {
    type: Phaser.WEBGL,
    width: 1280,
    height: 720,
    parent: 'phaser-example',
    scene: Example
};

const game = new Phaser.Game(config);

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

Параллельные фильтры в Phaser открывают мощный и относительно простой путь для создания пост-обработки прямо в основном цикле рендеринга камеры. Вы научились создавать эффект bloom вручную, настраивая порог яркости, размытие и режим смешивания, а также анимировать его. Для экспериментов попробуйте изменить значение в addThreshold (например, на 0.8), чтобы свечение затрагивало только самые яркие участки. Используйте другие режимы наложения из Phaser.BlendModes, такие как SCREEN или MULTIPLY, для разных визуальных стилей. Также поэкспериментируйте с добавлением других фильтров в цепочку parallelFilters.top, например, addColorMatrix() для тонирования свечения.