О чем этот пример
Создание статичного фона — это просто. Но как оживить сцену, добавив ей динамики и глубины без анимации спрайтов? В этом примере мы разберем, как использовать встроенный в Phaser генератор симплекс-шума для создания потрясающего эффекта "живой" поверхности с отражениями. Этот прием позволяет превратить обычную текстуру в нечто волнующееся и переливающееся, идеально подходящее для водных поверхностей, магических барьеров или атмосферных искажений, затрачивая минимум ресурсов на отрисовку.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
noise;
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('bg', 'assets/skies/spookysky.jpg');
}
create()
{
const { width, height } = this.scale;
this.noise = this.add.noisesimplex2d({
noiseCells: [ 4, 32 ],
noiseNormalMap: true,
noiseNormalScale: 0.3,
noiseWarpAmount: 1
}, 0, 0, width, height).setRenderToTexture('noise-normal');
// Create a blank texture.
this.textures.addFlatColor('blank', width, height, 0xffffff, 1);
const bg = this.add.image(width / 2, height / 2, 'blank').setFlipY(true);
bg.enableFilters().filters.internal.addImageLight({
environmentMap: 'bg',
normalMap: 'noise-normal',
bulge: 1
});
}
update (time)
{
const t = time / 800;
this.noise.noiseFlow = t % (Math.PI * 2);
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 1280,
height: 720,
scene: Example
};
const game = new Phaser.Game(config);
Подготовка сцены и загрузка ресурсов
В методе preload мы загружаем единственную текстуру — фоновое изображение неба. Обратите внимание на использование setBaseURL. Это позволяет задать базовый путь для всех последующих загрузок, что упрощает указание относительных путей к ресурсам.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.image('bg', 'assets/skies/spookysky.jpg');
}
Создание текстуры шума
Ключевой объект примера — this.noise. Мы создаем его с помощью this.add.noisesimplex2d. Этот метод генерирует двухмерную текстуру симплекс-шума.
Рассмотрим параметры конструктора:
* Первый аргумент — объект конфигурации.
* noiseCells: [4, 32] определяет размер ячеек шума по осям X и Y, влияя на крупность паттерна.
* noiseNormalMap: true — флаг, указывающий, что нужно создать не просто карту шума, а нормальную карту. Нормальная карта кодирует информацию о направлении поверхности для расчета освещения и отражений.
* noiseNormalScale: 0.3 — масштаб эффекта на нормальной карте. Чем больше значение, тем "выше" будут "волны".
* noiseWarpAmount: 1 — величина искажения шума.
* Следующие четыре аргумента (0, 0, width, height) задают позицию и размеры генерируемой области.
* Метод .setRenderToTexture('noise-normal') рендерит сгенерированную нормальную карту в текстуру с ключом 'noise-normal'. Эту текстуру мы будем использовать позже.
Также создается простая белая текстура 'blank' с помощью this.textures.addFlatColor, которая будет служить нашим холстом для эффекта.
create()
{
const { width, height } = this.scale;
this.noise = this.add.noisesimplex2d({
noiseCells: [ 4, 32 ],
noiseNormalMap: true,
noiseNormalScale: 0.3,
noiseWarpAmount: 1
}, 0, 0, width, height).setRenderToTexture('noise-normal');
// Create a blank texture.
this.textures.addFlatColor('blank', width, height, 0xffffff, 1);
}
Наложение фильтра отражения
Создаем изображение bg, используя нашу белую текстуру 'blank', и размещаем его по центру. Метод .setFlipY(true) переворачивает изображение по вертикали — это важно для корректного отображения отражения.
Затем мы включаем фильтры для этого изображения (enableFilters()) и добавляем конкретный фильтр — internal.addImageLight. Этот фильтр создает эффект отражения (рельефного освещения) на основе двух карт:
* environmentMap: 'bg' — текстура, которая будет "отражаться". Мы используем загруженное изображение неба.
* normalMap: 'noise-normal' — нормальная карта, которая задает форму поверхности. Именно наша сгенерированная карта шума создает иллюзию волн и неровностей.
* bulge: 1 — параметр, усиливающий эффект выпуклости/вогнутости.
const bg = this.add.image(width / 2, height / 2, 'blank').setFlipY(true);
bg.enableFilters().filters.internal.addImageLight({
environmentMap: 'bg',
normalMap: 'noise-normal',
bulge: 1
});
Анимация шума
Чтобы поверхность не была статичной, мы анимируем исходный шум в методе update. Мы изменяем свойство noiseFlow объекта this.noise, передавая в него значение, зависящее от времени.
time / 800 — это скорость течения. Деление на 800 замедляет анимацию, делая ее плавной. t % (Math.PI * 2) обеспечивает циклическое изменение значения в диапазоне от 0 до 2π, создавая бесшовную, зацикленную анимацию потока шума. Изменение noiseFlow приводит к смещению паттерна шума, что, в свою очередь, динамически меняет нормальную карту и, как следствие, отражение на основном изображении.
update (time)
{
const t = time / 800;
this.noise.noiseFlow = t % (Math.PI * 2);
}
Что попробовать дальше
Мы создали динамичный, "живой" фон, используя всего одну фоновую текстуру, генератор шума и фильтр отражения. Сила этого подхода — в его производительности и гибкости. Для экспериментов попробуйте изменить параметры noiseCells для получения более крупной или мелкой ряби, увеличьте noiseNormalScale для более агрессивных волн или подставьте другую текстуру в environmentMap, например, текстуру лавы или звездного неба, чтобы получить совершенно иной визуальный эффект.
