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

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

Версия 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('phaserlogo', 'assets/sprites/phaser2.png');
    }

    create ()
    {
        const phaserLogoImg = this.add.image(this.scale.width / 2, this.scale.height / 2 - 50, 'phaserlogo');
        const fxPixelated = phaserLogoImg.enableFilters().filters.internal.addPixelate(-1);

        // Text to show the current build target
        const shape = this.add.rectangle(this.sys.scale.width / 2, this.sys.scale.height / 2 + 160, this.sys.scale.width, 30, 0x000000, 0.8);
        const textDebug = this.add.text(this.sys.scale.width / 2, this.sys.scale.height / 2 + 160, `Pixel Amount:-1.00`, {fontSize: 25}).setOrigin(.5);


        // Loop to change the pixel amount
        this.time.addEvent({
            delay: 1000,
            callback: () => {
                this.tweens.addCounter({
                    from: -1,
                    to: 10,
                    duration: 2000,
                    yoyo: true,
                    repeat: -1,
                    onUpdate: (value) => {
                        fxPixelated.amount = value.getValue();
                        textDebug.setText(`Pixel Amount:${fxPixelated.amount.toFixed(2).padStart(5, " ")}`);
                    }
                })
            }
        });
    }
}

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

const game = new Phaser.Game(config);

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

Всё начинается в методе preload(). Здесь мы указываем базовый URL для загрузки ресурсов и загружаем одно изображение — логотип Phaser, которое будем обрабатывать.

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

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

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

В методе create() происходит основная магия. Сначала мы создаём изображение и центрируем его на экране с небольшим смещением по вертикали.

const phaserLogoImg = this.add.image(this.scale.width / 2, this.scale.height / 2 - 50, 'phaserlogo');

Затем мы включаем систему фильтров для этого конкретного объекта и добавляем к ней фильтр Pixelate. Цепочка вызовов выглядит так:

const fxPixelated = phaserLogoImg.enableFilters().filters.internal.addPixelate(-1);

Метод enableFilters() активирует конвейер фильтров для спрайта phaserLogoImg. Свойство filters.internal предоставляет доступ к внутреннему менеджеру фильтров этого объекта. Метод addPixelate(-1) создаёт и добавляет сам фильтр пикселизации, где аргумент -1 — начальное значение силы эффекта (amount). Важно сохранить возвращаемую ссылку fxPixelated — через неё мы будем управлять фильтром.

Интерфейс для отладки: текст и фон

Чтобы визуально отслеживать изменение параметра фильтра, пример создаёт простой UI. Сначала рисуется полупрозрачная черная панель с помощью add.rectangle.

const shape = this.add.rectangle(this.sys.scale.width / 2, this.sys.scale.height / 2 + 160, this.sys.scale.width, 30, 0x000000, 0.8);

Затем поверх неё создаётся текстовый объект, который будет отображать текущее значение.

const textDebug = this.add.text(this.sys.scale.width / 2, this.sys.scale.height / 2 + 160, `Pixel Amount:-1.00`, {fontSize: 25}).setOrigin(.5);

Метод setOrigin(.5) центрирует текст относительно его позиции, что упрощает выравнивание. Изначальный текст показывает стартовое значение amount равное -1.

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

Самая динамичная часть примера — это цикл, который плавно меняет силу пикселизации от -1 до 10 и обратно. Используется встроенный менеджер времени и система твинов Phaser.

this.time.addEvent({
    delay: 1000,
    callback: () => {
        this.tweens.addCounter({
            from: -1,
            to: 10,
            duration: 2000,
            yoyo: true,
            repeat: -1,
            onUpdate: (value) => {
                fxPixelated.amount = value.getValue();
                textDebug.setText(`Pixel Amount:${fxPixelated.amount.toFixed(2).padStart(5, " ")}`);
            }
        })
    }
});

time.addEvent с delay: 1000 запускает колбэк через секунду после старта сцены. Внутри создаётся твин для счётчика (addCounter). Параметры from и to задают диапазон значений. yoyo: true заставляет анимацию проигрываться в обратном порядке, а repeat: -1 делает цикл бесконечным.

В колбэке onUpdate мы получаем текущее значение твина через value.getValue() и присваиваем его свойству fxPixelated.amount. Это непосредственно меняет степень пикселизации на спрайте. Одновременно обновляется текст для отладки с форматированием до двух знаков после запятой.

Конфигурация игры: важная настройка pixelArt

Для корректного отображения пиксельной графики и фильтров в конфиге игры включён ключевой параметр.

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

Установка pixelArt: true отключает линейную интерполяцию текстур при масштабировании. Это критически важно, если вы работаете с пиксель-артом, чтобы сохранить чёткие границы пикселей. Также здесь задаётся тёмно-синий фон (backgroundColor), на котором хорошо видно логотип, и указывается родительский HTML-элемент (parent) для канваса.

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

Фильтр Pixelate в Phaser 3 — это простой, но эффективный способ добавить динамические визуальные искажения. Как показал пример, для его использования достаточно нескольких строк кода: включить фильтры для объекта, добавить эффект и управлять его свойством amount. Система твинов позволяет легко анимировать это свойство, создавая плавные переходы. Идеи для экспериментов: попробуйте применить фильтр не к статичному спрайту, а к анимированному персонажу. Свяжите значение amount со здоровьем игрока — чем оно меньше, тем сильнее пикселизация, создавая эффект ранения. Или используйте резкое изменение параметра для визуализации телепортации или сбоя в цифровом мире. Фильтр также можно комбинировать с другими, например, с Grayscale или Blur, для создания сложных составных эффектов.