О чем этот пример
Использование шейдеров позволяет вывести визуальную составляющую игры на новый уровень. Встроенный в Phaser инструмент `add.shader` даёт возможность применять GLSL-шейдеры к объектам, создавая динамичные фоны, магические эффекты и сложные трансформации прямо в Canvas или WebGL-контексте. В этой статье мы разберем, как загружать и настраивать шейдеры, управлять их параметрами во время выполнения и комбинировать с обычными спрайтами для создания захватывающей графики.
Версия 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.glsl('hannae', 'assets/shaders/love-u-hanne-e.frag');
this.load.glsl('yinyang', 'assets/shaders/yin-yang.frag');
this.load.image('block', 'assets/sprites/block.png');
}
create ()
{
const shader = this.add.shader({
name: 'hannae',
fragmentKey: 'hannae',
setupUniforms: (setUniform, drawingContext) => {
setUniform('time', this.game.loop.getDuration());
},
}, 400, 300, 800, 800);
this.add.image(200, 300, 'block');
const shader2 = this.add.shader({
name: 'yinyang',
fragmentKey: 'yinyang',
setupUniforms: (setUniform, drawingContext) => {
setUniform('time', this.game.loop.getDuration());
},
}, 400, 300, 256, 256);
this.add.image(400, 300, 'block');
this.add.image(600, 300, 'block');
this.tweens.add({
targets: shader2,
scaleX: 4,
scaleY: 4,
repeat: -1,
yoyo: true,
duration: 2000
});
this.input.on('pointermove', function (pointer)
{
shader2.setPosition(pointer.x, pointer.y);
});
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example,
};
const game = new Phaser.Game(config);
Загрузка ресурсов: шейдеры как ассеты
Перед использованием шейдеры необходимо загрузить как ресурсы. Phaser предоставляет для этого специальный метод load.glsl(). Он принимает уникальный ключ и путь к файлу с исходным кодом шейдера.
this.load.glsl('hannae', 'assets/shaders/love-u-hanne-e.frag');
this.load.glsl('yinyang', 'assets/shaders/yin-yang.frag');
В данном примере загружаются два фрагментных шейдера (.frag). Ключи 'hannae' и 'yinyang' будут использоваться для их идентификации при создании. Обратите внимание, что для работы с шейдерами рендерер игры должен быть Phaser.WEBGL (это указано в конфигурации type).
Создание и настройка шейдерного объекта
Основной метод для добавления шейдера на сцену — this.add.shader(). Он принимает объект конфигурации и параметры позиционирования.
const shader = this.add.shader({
name: 'hannae',
fragmentKey: 'hannae',
setupUniforms: (setUniform, drawingContext) => {
setUniform('time', this.game.loop.getDuration());
},
}, 400, 300, 800, 800);
В конфигурации:
- fragmentKey указывает на ключ загруженного шейдера.
- setupUniforms — это функция обратного вызова, которая выполняется каждый кадр для обновления uniform-переменных шейдера. Здесь переменной time передается общее время работы игры в миллисекундах, полученное через this.game.loop.getDuration(). Это стандартный приём для анимации шейдеров.
Последние четыре аргумента метода задают позицию (x, y) и размер (width, height) прямоугольной области, в которой будет отрисовываться шейдер.
Интеграция шейдеров и спрайтов
Шейдеры в Phaser — это полноценные игровые объекты (GameObject). Их можно добавлять на сцену вместе с обычными изображениями, и они будут отрисовываться в порядке добавления (z-order).
this.add.image(200, 300, 'block');
const shader2 = this.add.shader(...);
this.add.image(400, 300, 'block');
На примере видно, что блок, шейдер и еще один блок добавляются последовательно. Они могут перекрывать друг друга. Это открывает возможности для создания сложных композиций, где шейдеры служат динамическим фоном или эффектами поверх/под спрайтами.
Динамическое управление: твины и интерактивность
Как и любой GameObject, шейдеру можно анимировать свойства, например, масштаб, используя систему твинов Phaser.
this.tweens.add({
targets: shader2,
scaleX: 4,
scaleY: 4,
repeat: -1,
yoyo: true,
duration: 2000
});
Этот твин заставляет второй шейдер пульсировать, плавно увеличиваясь и уменьшаясь. Кроме того, шейдеры можно перемещать в реальном времени в ответ на действия игрока.
this.input.on('pointermove', function (pointer) {
shader2.setPosition(pointer.x, pointer.y);
});
Обработчик события pointermove вызывает метод setPosition() у объекта shader2, привязывая его положение к курсору мыши. Метод setPosition наследуется от базового класса GameObject.
Что попробовать дальше
Шейдеры в Phaser — это мощный инструмент для создания уникальной графики без необходимости погружаться в низкоуровневый WebGL. Вы можете анимировать их, комбинировать со спрайтами и управлять ими в реальном времени. Для экспериментов попробуйте: изменить uniform-переменные (например, добавить зависимость от разрешения экрана), применить шейдер не как фон, а как текстуру к спрайту, или создать цепочку из нескольких накладывающихся друг на друга шейдерных эффектов.
