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

Визуальные эффекты на основе процедурного шума — мощный инструмент для создания атмосферы в играх: от мерцающих энергетических полей до таинственных туманов. Phaser 3 предоставляет встроенные объекты `NoiseSimplex2D` и `NoiseSimplex3D`, которые генерируют текстуры шума прямо на GPU, что позволяет создавать сложные анимированные фоны без использования внешних изображений. В этой статье мы разберем, как настроить и анимировать эти объекты, управляя их параметрами в реальном времени для создания гипнотических потоковых эффектов.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    noise2d;
    noise3d;

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

        this.noise2d = this.add.noisesimplex2d({
            noiseCells: [ 8, 9 ],
            noiseIterations: 3,
            noiseColorStart: 0xffaa88,
            noiseColorEnd: 0x442266
        }, width / 4, height / 2, width / 2, height);

        this.noise3d = this.add.noisesimplex3d({
            noiseCells: [ 8, 9, 4 ],
            noiseIterations: 3,
            noiseColorStart: 0x22dd22,
            noiseColorEnd: 0x662244
        }, width * 3 / 4, height / 2, width / 2, height);

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

    update (time)
    {
        // Prevent long-term precision loss.
        // Flow is cyclic, every 2PI radians.
        const t = (time / 1000) % (Math.PI * 2);

        this.noise2d.noiseFlow = t;
        this.noise3d.noiseFlow = t;
    }
}

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

const game = new Phaser.Game(config);

Создание объектов шума: 2D и 3D

Класс Example расширяет Phaser.Scene и создаёт два визуальных объекта шума в методе create(). Один использует двумерный, а другой — трёхмерный симплекс-шум. Ключевая разница в измерении шума влияет на визуальную сложность и характер движения.

Для создания объекта используется фабричный метод this.add.noisesimplex2d() (или noisesimplex3d()). Первым параметром передаётся конфигурационный объект, а следующие четыре аргумента задают позицию (x, y) и размеры (width, height) прямоугольной области, в которой будет отрисован шум.

this.noise2d = this.add.noisesimplex2d({
    noiseCells: [ 8, 9 ],
    noiseIterations: 3,
    noiseColorStart: 0xffaa88,
    noiseColorEnd: 0x442266
}, width / 4, height / 2, width / 2, height);

Конфигурация: ячейки, итерации и цвета

Конфигурационный объект позволяет тонко настроить внешний вид шума.

* noiseCells: Массив, определяющий детализацию. Для 2D шума это [x, y], для 3D — [x, y, z]. Меньшие значения дают более крупные, размазанные "ячейки" шума, большие — более мелкую и частую текстуру. * noiseIterations: Количество наложений (октав) шума. Больше итераций увеличивает детализацию и контрастность текстуры. * noiseColorStart и noiseColorEnd: Цвета в формате HEX (например, 0xffaa88), между которыми интерполируется итоговая текстура. Они задают цветовую палитру визуализации.

{
    noiseCells: [ 8, 9, 4 ], // 3D: 8 по X, 9 по Y, 4 по Z
    noiseIterations: 3,
    noiseColorStart: 0x22dd22,
    noiseColorEnd: 0x662244
}

Анимация через свойство noiseFlow

Статичный шум — это лишь половина возможностей. Динамика создаётся в методе update(), который вызывается на каждом кадре.

Свойство noiseFlow объектов noise2d и noise3d управляет "потоком" или фазой шума. Изменяя это значение со временем, мы заставляем текстуру плавно перетекать и видоизменяться, создавая иллюзию движения, подобного текущей жидкости или полю энергии.

В примере для анимации используется время игры (time), которое преобразуется в циклическое значение. Операция модуля % (Math.PI * 2) гарантирует, что значение noiseFlow будет циклически повторяться в диапазоне от 0 до ~6.283, предотвращая переполнение и потерю точности при долгой работе.

update (time)
{
    const t = (time / 1000) % (Math.PI * 2);
    this.noise2d.noiseFlow = t;
    this.noise3d.noiseFlow = t;
}

Сравнение визуала 2D и 3D шума

Разместив оба объекта на сцене, можно сразу увидеть разницу. 2D шум (noiseCells: [8, 9]) создаёт более плоский, слоистый узор, который "течёт" в двух измерениях. 3D шум (noiseCells: [8, 9, 4]) использует третье измерение (Z) как дополнительную ось вариативности, что даёт более объёмный, сложный и органичный рисунок, напоминающий клубящийся дым или мрамор.

Именно третье измерение в конфигурации noiseCells и делает визуал 3D объекта богаче. Анимация noiseFlow по сути "пролистывает" срезы этого трёхмерного поля шума, создавая непрерывную трансформацию.

// 2D шум — плоский, волнообразный
this.add.text(10, 10, '2D', { fontSize: 24 });
// 3D шум — объёмный, турбулентный
this.add.text(width / 2 + 10, 10, '3D', { fontSize: 24 });

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

Объекты NoiseSimplex2D/3D в Phaser — это готовый и производительный способ добавить в игру "живые" фоны, эффекты магии, помех или природных явлений. Для экспериментов попробуйте: 1. Связывать noiseFlow не со временем, а с положением игрока, создавая реактивное поле вокруг него. 2. Использовать несколько слоёв шума с разными noiseCells и цветами, наложенными с режимами смешивания (setBlendMode). 3. Динамически менять noiseColorStart и noiseColorEnd в ответ на события в игре для визуальной обратной связи.