О чем этот пример
Фильтры камеры в 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), чтобы создать сложные композиционные эффекты.
