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

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

Версия 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('block', 'assets/pics/lance-overdose-loader-eye.png');
    }

    create ()
    {
        let amount = 0.5;

        for (let y = 0; y < 2; y++)
        {
            for (let x = 0; x < 3; x++)
            {
                const sprite = this.add.sprite(128 + x * 256, 128 + y * 256, 'block');

                sprite.enableFilters();

                // Enlarge filter view.
                sprite.focusFiltersOverride(undefined, undefined, sprite.width + 64, sprite.height + 64);

                sprite.filters.internal.addBarrel(amount);

                this.add.text(128 + x * 256, 128 + y * 256 + 96, amount).setOrigin(0.5, 0).setResolution(window.devicePixelRatio);

                amount += 0.25;
            }
        }
    }
}

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

const game = new Phaser.Game(config);

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

Как и в большинстве примеров Phaser, работа начинается в методе preload сцены. Здесь мы загружаем одно изображение, которое впоследствии будем искажать.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('block', 'assets/pics/lance-overdose-loader-eye.png');
}

Метод setBaseURL устанавливает базовый URL для всех последующих загрузок, что удобно, если все ресурсы хранятся в одном месте. Ключ 'block' — это внутреннее имя загруженного изображения, которое мы будем использовать для создания спрайтов.

Создание спрайтов и активация фильтров

В методе create мы размещаем несколько спрайтов на сцене в виде сетки 3x2. Каждому из них будет применен фильтр с разной силой эффекта.

let amount = 0.5;

for (let y = 0; y < 2; y++)
{
    for (let x = 0; x < 3; x++)
    {
        const sprite = this.add.sprite(128 + x * 256, 128 + y * 256, 'block');
        sprite.enableFilters();

Циклы создают 6 спрайтов, позиционируя их с шагом в 256 пикселей. Ключевой метод sprite.enableFilters() активирует систему фильтров для данного конкретного спрайта. Без этого вызова любые попытки добавить фильтр будут игнорироваться.

Настройка области фильтра и добавление эффекта

По умолчанию фильтр применяется только к области самого спрайта. Эффект "бочки" может искажать края за эти пределы, поэтому важно расширить область видимости фильтра.

sprite.focusFiltersOverride(undefined, undefined, sprite.width + 64, sprite.height + 64);
sprite.filters.internal.addBarrel(amount);

Метод focusFiltersOverride позволяет переопределить область, которую "видит" фильтр. Первые два аргумента undefined оставляют смещение (`x,y`) по умолчанию (центр спрайта). Третий и четвертый аргументы задают ширину и высоту новой области, увеличенные на 64 пикселя с каждой стороны. Это гарантирует, что искаженные края изображения не будут обрезаны.

Затем мы обращаемся к внутреннему менеджеру фильтров спрайта (sprite.filters.internal) и вызываем метод addBarrel(amount). Параметр amount управляет силой и типом искажения: * Положительные значения создают выпуклый эффект (как "рыбий глаз"). * Отрицательные значения создают вогнутый эффект. * Значение `0` не дает искажения. В нашем примере amount начинается с 0.5 и увеличивается на 0.25 для каждого следующего спрайта, что позволяет наглядно сравнить результат.

Визуализация параметров и конфигурация игры

Чтобы было понятно, какое значение amount соответствует каждому спрайту, под ним создается текстовый объект.

this.add.text(128 + x * 256, 128 + y * 256 + 96, amount).setOrigin(0.5, 0).setResolution(window.devicePixelRatio);

Метод setOrigin(0.5, 0) устанавливает точку привязки текста по горизонтали в центр, а по вертикали — в верхний край. Это центрирует текст под спрайтом. setResolution помогает правильно отображать текст на экранах с высоким DPI.

Финальная часть кода — стандартная конфигурация экземпляра игры Phaser.

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

const game = new Phaser.Game(config);

Здесь задается разрешение холста, фоновый цвет (backgroundColor), идентификатор HTML-контейнера (parent) и главная сцена.

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

Фильтр Barrel — это простой, но эффективный способ добавить динамические оптические искажения в вашу игру. Он работает непосредственно на GPU, поэтому производительность остается высокой даже при множестве обработанных спрайтов. Для экспериментов попробуйте: 1. Менять параметр amount в реальном времени в ответ на действия игрока (например, при активации "увеличения"). 2. Комбинировать addBarrel с другими фильтрами из sprite.filters.internal, такими как addGlow или addBloom. 3. Применять фильтр не ко всему спрайту, а только к его части, манипулируя параметрами focusFiltersOverride.