О чем этот пример
Визуальные эффекты, основанные на шуме, — мощный инструмент для создания динамических текстур, искажений и атмосферных явлений в играх. Phaser предоставляет встроенный объект Noise (`Phaser.GameObjects.Noise`), который генерирует шум Перлина и может использоваться как текстура или источник смещения для других игровых объектов. В этой статье мы разберем пример, который показывает, как создать, визуализировать и применить нормальный шум для искажения изображения, открывая путь к созданию эффектов воды, огня, дыма или разрушающихся поверхностей.
Версия 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('grad', 'assets/skies/fire.png');
}
create()
{
const { width, height } = this.scale;
this.add.noise({
noiseRandomNormal: true
}, 0, 0, width / 16 / 3, height / 16)
.setRenderToTexture('noise');
// The texture we'll distort.
const base = this.add.image(width * 1 / 6, height / 2, 'grad');
Phaser.Actions.FitToRegion(base, 0, { x: 0, y: 0, width: width / 3, height: height });
// Visualize the normal noise.
// Rendering to texture turns off rendering directly to the canvas.
const noise = this.add.image(width / 2, height / 2, 'noise');
Phaser.Actions.FitToRegion(noise, 0, { x: width / 3, y: 0, width: width / 3, height: height });
// Apply normal noise to the texture.
const image = this.add.image(width * 5 / 6, height / 2, 'grad');
Phaser.Actions.FitToRegion(image, 0, { x: width * 2 / 3, y: 0, width: width / 3, height: height });
image.enableFilters().filters.internal.addDisplacement('noise', 0.1, 0.1);
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 1280,
height: 720,
scene: Example
};
const game = new Phaser.Game(config);
Загрузка ресурсов и создание объекта шума
В методе preload загружается фоновая текстура 'grad', которая будет использоваться для демонстрации искажения. Ключевой момент происходит в create: создается объект шума с помощью фабричного метода this.add.noise().
this.add.noise({
noiseRandomNormal: true
}, 0, 0, width / 16 / 3, height / 16)
.setRenderToTexture('noise');
Конфигурационный объект { noiseRandomNormal: true } указывает, что шум должен генерироваться в нормальном (Normal) распределении, что дает более плавные и органичные переходы по сравнению с равномерным. Параметры 0, 0, width / 16 / 3, height / 16 задают позицию (x, y), ширину и высоту генерируемой карты шума. Ширина делится на 48 (width / 16 / 3), а высота — на 16, что создает прямоугольную текстуру. Метод .setRenderToTexture('noise') рендерит этот шум не на канвас напрямую, а в текстуру с именем 'noise', чтобы её можно было повторно использовать.
Подготовка и визуализация исходных элементов
Прежде чем применять шум, нужно подготовить сцену. Создаются три изображения, которые будут размещены горизонтально.
const base = this.add.image(width * 1 / 6, height / 2, 'grad');
Phaser.Actions.FitToRegion(base, 0, { x: 0, y: 0, width: width / 3, height: height });
Первое изображение base — это оригинальная текстура 'grad', размещенная в левой трети экрана. Phaser.Actions.FitToRegion масштабирует и позиционирует изображение так, чтобы оно заполнило заданную область (регион) с координатами { x: 0, y: 0, width: width / 3, height: height }.
const noise = this.add.image(width / 2, height / 2, 'noise');
Phaser.Actions.FitToRegion(noise, 0, { x: width / 3, y: 0, width: width / 3, height: height });
Второе изображение noise визуализирует саму текстуру шума, которую мы создали на предыдущем шаге. Оно размещается в центральной трети экрана. Это позволяет наглядно увидеть, как выглядит сгенерированный нормальный шум.
Применение шума как карты смещения (Displacement Map)
Самое интересное — использование текстуры шума для искажения другого изображения. Это делается с помощью фильтра смещения (Displacement Filter).
const image = this.add.image(width * 5 / 6, height / 2, 'grad');
Phaser.Actions.FitToRegion(image, 0, { x: width * 2 / 3, y: 0, width: width / 3, height: height });
image.enableFilters().filters.internal.addDisplacement('noise', 0.1, 0.1);
Создается третье изображение image с той же текстурой 'grad' и размещается в правой трети экрана. Метод image.enableFilters() активирует систему фильтров для этого игрового объекта. Затем через внутренний менеджер фильтров filters.internal добавляется фильтр смещения: .addDisplacement('noise', 0.1, 0.1).
Первый аргумент 'noise' — это ключ текстуры, которая будет использоваться как карта смещения (та самая, которую мы создали и сохранили). Второй и третий аргументы (0.1, 0.1) — это множители (scale) смещения по осям X и Y. Они определяют силу искажения. Фильтр берет значения из текстуры шума (где каждый пиксель содержит информацию о смещении) и применяет их к пикселям исходного изображения, создавая эффект волн, ряби или деформации.
Конфигурация игры и инициализация
Код завершается стандартной конфигурацией игры Phaser.
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 1280,
height: 720,
scene: Example
};
const game = new Phaser.Game(config);
Важно указать type: Phaser.WEBGL, так как фильтры (включая смещение) требуют WebGL-рендерера. Параметры width и height задают разрешение игрового поля. Класс сцены Example передается в конфигурацию, и игра инициализируется созданием экземпляра Phaser.Game.
Что попробовать дальше
Объект Noise в Phaser — это не просто визуализация шума, а полноценный источник данных для постобработки. Используя его как карту смещения, вы можете легко создавать сложные эффекты искажения для воды, магии, теплового марева или повреждений. Для экспериментов попробуйте: изменить параметры noiseRandomNormal на false для равномерного шума; увеличить множители в .addDisplacement() для более сильного искажения; анимировать позицию или масштаб объекта шума для создания движущихся волн; или применить шум к спрайтам анимации для эффекта "растворения".
