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

Фильтры камеры в Phaser позволяют применять графические эффекты ко всему отображаемому миру, не затрагивая исходные текстуры. В этом примере мы рассмотрим фильтр 'Blocky', который искусственно снижает разрешение изображения, создавая стилистику старых пиксельных игр или целенаправленно грубую графику. Вы научитесь добавлять такой фильтр к камере, управлять его параметрами и динамически включать/выключать по клику, что полезно для создания режимов 'ностальгии' или визуальных переключателей в игре.

Версия 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('sonic', 'assets/sprites/sonic.png');
        this.load.image('phaser-dude', 'assets/sprites/phaser-dude.png');
        this.load.image('shinyball', 'assets/sprites/shinyball.png');
        this.load.image('sky', 'assets/skies/ms3-sky.png');
    }

    create ()
    {
        this.add.image(640, 360, 'sky').setScale(2.5).setScrollFactor(0);

        const addSprite = (x, y, distance) => {
            const texture = Phaser.Math.RND.pick([
                'sonic',
                'phaser-dude',
                'shinyball'
            ]);
            const sprite = this.add.sprite(x, y, texture);
            sprite
                .setScale(1 / distance)
                .setScrollFactor(1 / distance)
                .setOrigin(0.5, 1);
        };

        for (let x = 0; x < 1280; x += 64)
        {
            addSprite(x, 160, 1);
        }

        for (let x = 0; x < 1280; x += 128)
        {
            addSprite(x, 280, 1/2);
        }

        for (let x = 0; x < 1280; x += 256)
        {
            addSprite(x, 480, 1/4);
        }

        for (let x = 0; x < 1280; x += 512)
        {
            addSprite(x, 840, 1/8);
        }

        this.blockyController = this.cameras.main.filters.external.addBlocky({ size: 4});

        // Toggle the filter on and off
        this.input.on('pointerup', () => {
            this.blockyController.setActive(!this.blockyController.active);
        });
    }

    update (time, delta)
    {
        const camera = this.cameras.main;

        camera.scrollX = Math.sin(time / 4000) * 20;
    }
}

const config = {
    type: Phaser.AUTO,
    width: 1280,
    height: 720,
    backgroundColor: '#000000',
    parent: 'phaser-example',
    scene: Example,
    antialias: false
};

let game = new Phaser.Game(config);

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

В методе preload загружаются изображения, которые будут использоваться в сцене. Обратите внимание на использование this.load.setBaseURL для указания базового пути к ресурсам. Это удобно, когда все ассеты хранятся в одном удалённом каталоге.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.image('sonic', 'assets/sprites/sonic.png');
    this.load.image('phaser-dude', 'assets/sprites/phaser-dude.png');
    this.load.image('shinyball', 'assets/sprites/shinyball.png');
    this.load.image('sky', 'assets/skies/ms3-sky.png');
}

Создание мира с параллакс-эффектом

В create сначала добавляется фоновое изображение неба. Оно масштабируется и устанавливается с setScrollFactor(0), что означает, что этот слой не будет двигаться при скролле камеры, создавая статичный фон.

Далее определяется функция addSprite. Её ключевая задача — создать спрайт и настроить для него эффект перспективы (псевдо-3D) через масштаб и фактор скролла. Чем больше distance (меньше значение), тем дальше должен казаться объект: он будет меньше и медленнее двигаться при скролле камеры. Текстура выбирается случайным образом из массива.

const addSprite = (x, y, distance) => {
    const texture = Phaser.Math.RND.pick([
        'sonic',
        'phaser-dude',
        'shinyball'
    ]);
    const sprite = this.add.sprite(x, y, texture);
    sprite
        .setScale(1 / distance)
        .setScrollFactor(1 / distance)
        .setOrigin(0.5, 1);
};

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

Добавление и управление фильтром Blocky

Основной графический эффект достигается добавлением фильтра к главной камере. Фильтры камеры находятся в свойстве this.cameras.main.filters. Обратитесь к внешнему (external) менеджеру фильтров и вызовите метод addBlocky. Ему передаётся объект конфигурации, где ключевой параметр — size. Он определяет размер пиксельного блока. Возвращаемый контроллер (blockyController) позволяет управлять фильтром.

this.blockyController = this.cameras.main.filters.external.addBlocky({ size: 4});

Фильтр можно динамически включать и выключать. В примере это сделано по клику мыши (или касанию). При срабатывании события 'pointerup' состояние фильтра переключается на противоположное.

this.input.on('pointerup', () => {
    this.blockyController.setActive(!this.blockyController.active);
});

Анимированный скролл камеры

В методе update реализована простая анимация движения камеры по горизонтали. Свойство camera.scrollX меняется по синусоидальному закону в зависимости от времени. Это заставляет весь мир с параллакс-слоями плавно колебаться из стороны в сторону, наглядно демонстрируя работу setScrollFactor у спрайтов.

update (time, delta)
{
    const camera = this.cameras.main;
    camera.scrollX = Math.sin(time / 4000) * 20;
}

Конфигурация игры

Обратите внимание на два важных параметра в объекте конфигурации игры (config). Поле antialias установлено в false. Это отключает сглаживание на уровне рендерера, что делает пикселизацию от фильтра Blocky более чёткой и соответствующей задумке. Параметр backgroundColor задаёт чёрный фон, который будет виден, если сцена не заполняет всё окно.

const config = {
    type: Phaser.AUTO,
    width: 1280,
    height: 720,
    backgroundColor: '#000000',
    parent: 'phaser-example',
    scene: Example,
    antialias: false // Важно для пиксельного стиля!
};

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

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