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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    gradient;

    preload ()
    {
        
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('sky', 'assets/skies/chrome.png');
    }

    create ()
    {
        this.gradient = this.add.gradient({
            repeatMode: 3, // TRIANGULAR
            start: { x: 0.5, y: 0.5 },
            shape: { x: 0.02, y: 0 },
            shapeMode: 2, // RADIAL
            dither: true,
            bands: [
                {
                    colorStart: 0xffffff,
                    colorEnd: 0x000000,
                    interpolation: 2
                }
            ]
        }, 400, 300, 800, 600);

        // Necessary for captureFrame:
        this.cameras.main.setForceComposite(true);
        this.add.captureFrame('ripples');

        this.add.image(400, 300, 'sky')
        .setScale(1.1)
        .setAlpha(0.9)
        .enableFilters()
        .filters.internal.addDisplacement('ripples');
    }

    update (time)
    {
        this.gradient.offset = time / 400;
    }
}

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

const game = new Phaser.Game(config);

Создание объекта Gradient

Основной инструмент в нашем примере — объект Gradient. Он создается в методе create() сцены с помощью метода this.add.gradient(). Этот метод принимает объект конфигурации и параметры размера.

Конфигурация градиента задает его ключевые визуальные свойства:

this.gradient = this.add.gradient({
    repeatMode: 3, // TRIANGULAR
    start: { x: 0.5, y: 0.5 },
    shape: { x: 0.02, y: 0 },
    shapeMode: 2, // RADIAL
    dither: true,
    bands: [
        {
            colorStart: 0xffffff,
            colorEnd: 0x000000,
            interpolation: 2
        }
    ]
}, 400, 300, 800, 600);
- `repeatMode: 3`: Устанавливает треугольный режим повторения градиента (`Phaser.Types.GameObjects.Gradient.GRADIENT_REPEAT.TRIANGULAR`). Это создает плавные волны от центра к краям.
- `start: { x: 0.5, y: 0.5 }`: Определяет точку начала градиента в центре объекта (координаты от 0 до 1).
- `shape: { x: 0.02, y: 0 }` и `shapeMode: 2`: Вместе задают радиальную форму (`RADIAL`) градиента. Параметр `shape.x` влияет на «растяжение» градиента.
- `dither: true`: Включает дизеринг для сглаживания цветовых переходов и уменьшения полос.
- `bands`: Массив, определяющий цветовые полосы. В примере одна полоса — плавный переход от белого (`0xffffff`) к черному (`0x000000`) с интерполяцией.
Последние четыре числа (`400, 300, 800, 600`) — это координаты `x`, `y`, ширина и высота объекта градиента на сцене.

Использование градиента как карты смещения

Сам по себе анимированный градиент может служить фоном. Но в примере показана более продвинутая техника: использование его в качестве карты смещения (displacement map) для другого графического объекта. Этот эффект создает иллюзию волн или ряби на изображении.

Сначала необходимо подготовить градиент к использованию в качестве текстуры для фильтра. Для этого используется система захвата кадров (CaptureFrame).

// Необходимо для корректной работы захвата кадра:
this.cameras.main.setForceComposite(true);
this.add.captureFrame('ripples');

Метод setForceComposite(true) гарантирует, что градиент будет отрендерен в отдельный слой. Метод this.add.captureFrame('ripples') создает текстуру с именем 'ripples', в которую будет постоянно записываться текущий кадр градиента.

Затем создается фоновое изображение (sky), и к нему применяется фильтр смещения, который использует нашу текстуру 'ripples' в качестве карты.

this.add.image(400, 300, 'sky')
.setScale(1.1)
.setAlpha(0.9)
.enableFilters()
.filters.internal.addDisplacement('ripples');

Цепочка методов: - .enableFilters() — активирует систему фильтров для этого игрового объекта. - .filters.internal.addDisplacement('ripples') — добавляет фильтр смещения и указывает, что в качестве карты смещения (displacementMap) следует использовать текстуру с именем 'ripples', которая автоматически обновляется нашим градиентом.

Анимация градиента в реальном времени

Динамика эффекта достигается за счет изменения свойства градиента в методе update(), который вызывается на каждом кадре игры.

update (time)
{
    this.gradient.offset = time / 400;
}

Параметр time — это встроенная переменная Phaser, содержащая текущее время игры в миллисекундах. Присваивая значение time / 400 свойству this.gradient.offset, мы плавно сдвигаем (смещаем) градиент. Поскольку градиент имеет треугольный режим повторения (TRIANGULAR), это смещение создает плавную пульсирующую волну, расходящуюся от центра.

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

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

Для запуска примера необходима стандартная конфигурация игры Phaser.

const config = {
    type: Phaser.WEBGL, // Важно использовать WEBGL для работы с градиентами и фильтрами
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    scene: Example // Наша сцена с градиентом
};

const game = new Phaser.Game(config);

Ключевой момент — использование type: Phaser.WEBGL. Градиенты и многие продвинутые эффекты, включая фильтры, работают только в контексте WebGL-рендерера Phaser. Рендерер Canvas их не поддерживает.

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

Объект Gradient в Phaser — мощный инструмент для создания динамичной графики без использования внешних ресурсов. Как показано в примере, его можно не только отрисовывать напрямую, но и использовать в качестве живой текстуры для пост-обработки других объектов через фильтры. **Идеи для экспериментов:** 1. Измените параметры bands, добавив несколько цветовых полос или изменив алгоритм интерполяции. 2. Поэкспериментируйте с shapeMode и repeatMode, чтобы получить линейные, конусные или другие типы градиентов. 3. Анимируйте не только offset, но и другие свойства, например shape.x или start, чтобы градиент "дышал" или двигался по сложной траектории. 4. Используйте несколько градиентов с разными настройками и наложите их друг на друга с помощью режимов наложения (blendMode). 5. Примените градиент как карту смещения к спрайту персонажа для создания эффекта теплового марева или искажения при получении урона.