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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('pic', 'assets/pics/anime-arcade-ai.jpg');
    }

    create ()
    {
        const sprite = this.add.image(400, 300, 'pic');

        this.add.text(10, 10, 'Mouse to move vignette. Mouse wheel to adjust radius');

        const fx = sprite.enableFilters().filters.internal.addVignette();

        fx.radius = 0.3;
        fx.strength = 0.425;
        fx.color.setTo(0, 0, 0, 0); // Vignette alpha.

        sprite.setInteractive();

        sprite.on('pointermove', (pointer, x, y) => {

            fx.x = x / 512;
            fx.y = y / 512;

        });

        sprite.on('wheel', (pointer, x, y) => {

            fx.radius += y * -0.001;

            fx.radius = Phaser.Math.Clamp(fx.radius, 0, 1);

        });
    }
}

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

let game = new Phaser.Game(config);

Загрузка ресурсов и создание спрайта

Всё начинается в методе preload, где мы загружаем фоновое изображение. В create это изображение размещается в центре сцены как обычный спрайт.

const sprite = this.add.image(400, 300, 'pic');

Важный момент: для работы с фильтрами спрайт должен быть особым типом — Image (или Sprite) с поддержкой WebGL. Обычный this.add.image подходит. Далее мы добавляем текстовую подсказку для пользователя.

Включение фильтров и добавление виньетки

Чтобы применить фильтр, спрайту нужно включить их поддержку. Метод .enableFilters() активирует систему фильтров для этого объекта и возвращает менеджер фильтров. Через свойство .filters.internal мы получаем доступ к внутренней системе и добавляем эффект виньетки.

const fx = sprite.enableFilters().filters.internal.addVignette();

Переменная fx теперь хранит экземпляр фильтра Vignette, через который мы управляем его параметрами. Сразу зададим базовые настройки: радиус, силу и цвет. Обратите внимание, что цвет задаётся как RGBA, и мы устанавливаем альфа-канал в 0, оставляя цвет чёрным, но полностью прозрачным — сама виньетка создаётся алгоритмически.

fx.radius = 0.3;
fx.strength = 0.425;
fx.color.setTo(0, 0, 0, 0);

Интерактивность: перемещение виньетки

Делаем спрайт интерактивным, чтобы он реагировал на события мыши. Затем вешаем обработчик на событие pointermove.

sprite.setInteractive();

sprite.on('pointermove', (pointer, x, y) => {
    fx.x = x / 512;
    fx.y = y / 512;
});

Координаты `xиyв колбэке — это локальные координаты указателя относительно спрайта. Свойства фильтраfx.xиfx.y` ожидают нормализованные значения (от 0 до 1), где (0,0) — это левый верхний угол спрайта, а (1,1) — правый нижний. Поэтому мы делим координаты на 512 (предполагаемая ширина текстуры/изображения). В вашем случае это значение может быть другим, его стоит подбирать эмпирически или вычислять на основе размера спрайта.

Интерактивность: изменение радиуса

Для изменения радиуса виньетки используем событие wheel. Свойство `y` в объекте события содержит величину прокрутки.

sprite.on('wheel', (pointer, x, y) => {
    fx.radius += y * -0.001;
    fx.radius = Phaser.Math.Clamp(fx.radius, 0, 1);
});

Мы изменяем fx.radius на величину прокрутки, умноженную на -0.001 (инверсия знака и коэффициент для плавности). Затем обязательно ограничиваем значение функцией Phaser.Math.Clamp между 0 и 1, так как радиус — это нормализованный параметр.

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

Вы только что реализовали динамический виньеточный фильтр, управляемый пользователем. Этот приём можно использовать для создания мини-игр с фонариком в тёмной комнате, эффекта туннельного зрения или просто как часть фоторедактора в игре. Поэкспериментируйте: привяжите силу виньетки fx.strength к геймпаду, измените fx.color на красный для эффекта ранения или анимируйте параметры со временем для плавного появления затемнения.