О чем этот пример

Симуляция природных текстур, таких как облака, вода или мрамор, часто требует большего, чем просто статичный шум. Ключ к реализму лежит в управлении итерациями шума — процессом наложения нескольких слоёв (октав) с разными параметрами. Эта статья наглядно демонстрирует, как с помощью объекта `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, вы получаете тонкий контроль над тем, как выглядит и движется итоговая картинка. Для экспериментов попробуйте: создать плавный переход между разными режимами шума по времени, использовать маску для комбинации нескольких шумовых объектов в одном или привязать параметры мощности к движениям игрока для реактивного игрового мира.