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

Визуальные эффекты, такие как дым, огонь, вода или органические текстуры, часто требуют алгоритмической генерации. Phaser предоставляет мощный инструмент `add.noisecell4d` для создания 4D клеточного шума прямо в Canvas или WebGL. Эта статья покажет, как управлять параметром `noiseIterations` для получения текстур разной детализации и плотности, что идеально подходит для фонов, анимаций частиц или procedural generation.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class Example extends Phaser.Scene
{
    noise1;
    noise2;
    noise3;

    create()
    {
        const { width, height } = this.scale;

        const config = {
            noiseCells: [ 5, 9, 4, 4 ],
            noiseColorStart: 0xffffff,
            noiseColorEnd: [ 0, 0, 0, 0 ]
        };

        this.noise1 = this.add.noisecell4d(config, width * 1 / 6, height / 2, width / 3, height);
        this.noise2 = this.add.noisecell4d({
            ...config,
            noiseIterations: 2
        }, width * 3 / 6, height / 2, width / 3, height);
        this.noise3 = this.add.noisecell4d({
            ...config,
            noiseIterations: 5
        }, width * 5 / 6, height / 2, width / 3, height);

        this.add.text(10, 10, '1 iteration', { fontSize: 24 }).setStroke('#ff8844', 2).setShadow(2, 2, '#333333', 2, true, false);
        this.add.text(width / 3 + 10, 10, '2 iterations', { fontSize: 24 }).setStroke('#ff8844', 2).setShadow(2, 2, '#333333', 2, true, false);
        this.add.text(width * 2 / 3 + 10, 10, '5 iterations', { fontSize: 24 }).setStroke('#ff8844', 2).setShadow(2, 2, '#333333', 2, true, false);
    }

    update (time)
    {
        const t = time / 10000;
        const c = Math.cos(t);
        const s = Math.sin(t);

        this.noise1.noiseOffset[0] = c;
        this.noise1.noiseOffset[1] = s;
        this.noise1.noiseOffset[2] = c / 4;
        this.noise1.noiseOffset[3] = s / 4;

        this.noise2.noiseOffset[0] = c;
        this.noise2.noiseOffset[1] = s;
        this.noise2.noiseOffset[2] = c / 4;
        this.noise2.noiseOffset[3] = s / 4;

        this.noise3.noiseOffset[0] = c;
        this.noise3.noiseOffset[1] = s;
        this.noise3.noiseOffset[2] = c / 4;
        this.noise3.noiseOffset[3] = s / 4;
    }
}

const config = {
    type: Phaser.WEBGL,
    parent: 'phaser-example',
    width: 1280,
    height: 720,
    scene: Example
};

const game = new Phaser.Game(config);

Основы: создание шумового объекта

Ключевой метод для работы — this.add.noisecell4d(). Он принимает конфигурационный объект, координаты, ширину и высоту, возвращая игровой объект с динамической текстурой.

Базовый конфиг определяет начальные ячейки и градиент цвета. Обратите внимание: noiseColorEnd задаётся как массив RGBA, где 0 соответствует полной прозрачности.

const config = {
    noiseCells: [ 5, 9, 4, 4 ],
    noiseColorStart: 0xffffff,
    noiseColorEnd: [ 0, 0, 0, 0 ]
};

Создадим первый объект шума, разместив его в левой трети экрана:

this.noise1 = this.add.noisecell4d(config, width * 1 / 6, height / 2, width / 3, height);

Магия итераций: noiseIterations

Параметр noiseIterations — это количество проходов алгоритма шума. Чем больше итераций, тем детальнее и "гуще" становится итоговая текстура. Это похоже на наслоение нескольких шумовых карт.

В примере мы создаём три объекта с разным количеством итераций, используя spread-оператор для копирования базового конфига. Первый объект использует значение по умолчанию (1 итерация), второй — 2, третий — 5.

this.noise2 = this.add.noisecell4d({
    ...config,
    noiseIterations: 2
}, width * 3 / 6, height / 2, width / 3, height);

this.noise3 = this.add.noisecell4d({
    ...config,
    noiseIterations: 5
}, width * 5 / 6, height / 2, width / 3, height);

На практике вы увидите, как текстура слева (1 итерация) — самая простая и разреженная, а справа (5 итераций) — наиболее детализированная и плотная.

Оживляем шум: анимация через noiseOffset

Статичный шум — это скучно. Сила 4D-шума в том, что у него есть четвёртое измерение, которое можно использовать как время. Массив noiseOffset объекта шума позволяет сдвигать шум в его 4D-пространстве, создавая плавную анимацию.

В методе update() мы вычисляем значения на основе времени, используя Math.cos() и Math.sin(). Эти значения затем присваиваются элементам массива noiseOffset. Первые два элемента часто отвечают за смещение по X и Y в 2D-пространстве, а третий и четвёртый — за смещение в дополнительных измерениях шума, что даёт более сложные и органичные паттерны движения.

update (time)
{
    const t = time / 10000;
    const c = Math.cos(t);
    const s = Math.sin(t);

    this.noise1.noiseOffset[0] = c;
    this.noise1.noiseOffset[1] = s;
    this.noise1.noiseOffset[2] = c / 4;
    this.noise1.noiseOffset[3] = s / 4;
    // ... тоже самое для noise2 и noise3
}

Деление значений для третьего и четвёртого смещения (c / 4, s / 4) замедляет изменение по этим осям, добавляя дополнительный слой сложности к анимации.

Практическое применение в играх

Как использовать этот инструмент?

* **Фоновые текстуры:** Быстро создавайте бесшовные, анимированные фоны для подводных уровней, космоса или магических порталов. * **Эффекты частиц:** Используйте шум как маску или текстуру для систем частиц, чтобы имитировать клубящийся дым или текущую лаву. * **Procedural generation:** Сгенерируйте карту высот или влажности для процедурно создаваемого мира. Несколько итераций шума могут имитировать горные хребты, а одна — равнины.

Экспериментируйте с массивом noiseCells. Эти четыре числа определяют начальное "зерно" или паттерн ячеек. Изменение даже одного числа кардинально меняет итоговый вид текстуры.

// Эксперимент с разными начальными ячейками
const experimentalConfig = {
    noiseCells: [ 2, 7, 1, 8 ], // Меняйте эти значения!
    noiseColorStart: 0x44aaff,
    noiseColorEnd: [ 0, 0, 0, 0 ],
    noiseIterations: 3
};

Что попробовать дальше

Метод add.noisecell4d в Phaser — это gateway drug в мир процедурной графики. Управляя noiseIterations и анимируя noiseOffset, вы получаете мощный контроль над сложностью и динамикой текстур. Для экспериментов попробуйте: связать скорость анимации с игровыми событиями (например, ускорить шум при получении урона), комбинировать несколько слоёв шума с разными цветами или использовать значение шума в конкретной точке (через возможные методы объекта, не показанные в примере) для логики игры — например, для определения, находится ли персонаж в 'тумане'.