О чем этот пример
Симуляция природных текстур, таких как облака, вода или мрамор, часто требует большего, чем просто статичный шум. Ключ к реализму лежит в управлении итерациями шума — процессом наложения нескольких слоёв (октав) с разными параметрами. Эта статья наглядно демонстрирует, как с помощью объекта `NoiseSimplex2D` в Phaser управлять детализацией, вкладом и скоростью изменения шума, создавая динамичные и визуально богатые эффекты для ваших игр.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
noise1;
noise2;
noise3;
noise4;
create()
{
const { width, height } = this.scale;
// Four variants, differing only in noise adjustments.
this.noise1 = this.add.noisesimplex2d({
noiseCells: [ 4, 9 ],
noiseIterations: 2
}, width * 1 / 8, height / 2, width / 4, height);
this.noise2 = this.add.noisesimplex2d({
noiseCells: [ 4, 9 ],
noiseIterations: 2,
noiseDetailPower: 4 // Increase detail quickly.
}, width * 3 / 8, height / 2, width / 4, height);
this.noise3 = this.add.noisesimplex2d({
noiseCells: [ 4, 9 ],
noiseIterations: 2,
noiseContributionPower: 1 // Subsequent octaves decay slower.
}, width * 5 / 8, height / 2, width / 4, height);
this.noise4 = this.add.noisesimplex2d({
noiseCells: [ 4, 9 ],
noiseIterations: 2,
noiseFlowPower: 8 // Subsequent octaves flow faster
}, width * 7 / 8, height / 2, width / 4, height);
this.add.text(10, 10, 'Default iteration', { fontSize: 24 }).setStroke('#ff8844', 2).setShadow(2, 2, '#333333', 2, true, false);
this.add.text(width / 4 + 10, 10, 'Detail +', { fontSize: 24 }).setStroke('#ff8844', 2).setShadow(2, 2, '#333333', 2, true, false);
this.add.text(width / 2 + 10, 10, 'Contribution decay -', { fontSize: 24 }).setStroke('#ff8844', 2).setShadow(2, 2, '#333333', 2, true, false);
this.add.text(width * 3 / 4 + 10, 10, 'Flow +', { fontSize: 24 }).setStroke('#ff8844', 2).setShadow(2, 2, '#333333', 2, true, false);
}
update (time)
{
const t = (time / 1000) % (Math.PI * 2);
this.noise1.noiseFlow = t;
this.noise2.noiseFlow = t;
this.noise3.noiseFlow = t;
this.noise4.noiseFlow = t;
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 1280,
height: 720,
scene: Example
};
const game = new Phaser.Game(config);
Создание базового шума
В примере создаются четыре объекта шума с помощью метода this.add.noisesimplex2d(). Каждый объект — это визуализация двухмерного симплекс-шума. Основные настройки задаются в конфигурационном объекте, а позиция и размер — следующими аргументами.
this.noise1 = this.add.noisesimplex2d({
noiseCells: [ 4, 9 ],
noiseIterations: 2
}, width * 1 / 8, height / 2, width / 4, height);
Параметр noiseCells определяет количество ячеек шума по осям X и Y, влияя на базовый масштаб паттерна. noiseIterations задаёт количество октав — слоёв шума, которые будут накладываться друг на друга. Первый объект — это наша контрольная точка с настройками по умолчанию.
Усиление детализации с `noiseDetailPower`
Второй объект шума использует параметр noiseDetailPower. Этот параметр контролирует, насколько быстро увеличивается частота (детализация) с каждой последующей октавой.
this.noise2 = this.add.noisesimplex2d({
noiseCells: [ 4, 9 ],
noiseIterations: 2,
noiseDetailPower: 4 // Increase detail quickly.
}, width * 3 / 8, height / 2, width / 4, height);
Значение `4` означает, что частота каждой следующей октавы будет увеличиваться в 4 раза сильнее, чем при стандартном линейном росте. Это быстро добавляет много мелких деталей, делая текстуру более сложной и «шероховатой».
Контроль вклада октав через `noiseContributionPower`
Третий объект меняет правило затухания амплитуды октав с помощью noiseContributionPower. Обычно каждая следующая октава вносит меньший вклад в общую картину.
this.noise3 = this.add.noisesimplex2d({
noiseCells: [ 4, 9 ],
noiseIterations: 2,
noiseContributionPower: 1 // Subsequent octaves decay slower.
}, width * 5 / 8, height / 2, width / 4, height);
Значение `1` (меньше стандартного) ослабляет скорость этого затухания. В результате вторая октава вносит больший вклад, чем обычно, создавая более контрастную и выраженную текстуру с сильными низкочастотными компонентами.
Ускорение потока шума с `noiseFlowPower`
Четвёртый объект использует noiseFlowPower. Этот параметр влияет на анимированный шум, управляемый свойством noiseFlow.
this.noise4 = this.add.noisesimplex2d({
noiseCells: [ 4, 9 ],
noiseIterations: 2,
noiseFlowPower: 8 // Subsequent octaves flow faster
}, width * 7 / 8, height / 2, width / 4, height);
Значение `8` означает, что скорость «потока» (смещения) для каждой последующей октавы будет увеличиваться в 8 раз. В анимации это создаст эффект, где мелкие детали (высшие октавы) движутся намного быстрее, чем крупные формы (низшие октавы), имитируя сложное движение, например, в облаках или воде.
Анимация шума в реальном времени
Динамика — это то, что оживляет текстуру. В методе update мы берём текущее время, преобразуем его в циклическое значение и присваиваем свойству noiseFlow каждого объекта шума.
update (time)
{
const t = (time / 1000) % (Math.PI * 2);
this.noise1.noiseFlow = t;
this.noise2.noiseFlow = t;
this.noise3.noiseFlow = t;
this.noise4.noiseFlow = t;
}
Свойство noiseFlow смещает координаты выборки шума, создавая иллюзию плавного движения или «течения» текстуры. Все четыре объекта анимируются одним значением `t, что позволяет наглядно сравнивать, как разные параметры мощности (noiseFlowPower`) влияют на визуальный характер этого движения.
Что попробовать дальше
Итерационный шум в Phaser — мощный инструмент для генерации процедурных текстур. Меняя noiseDetailPower, noiseContributionPower и noiseFlowPower, вы получаете тонкий контроль над тем, как выглядит и движется итоговая картинка. Для экспериментов попробуйте: создать плавный переход между разными режимами шума по времени, использовать маску для комбинации нескольких шумовых объектов в одном или привязать параметры мощности к движениям игрока для реактивного игрового мира.
