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

Статичные фоны уходят в прошлое. Создание динамичных, дышащих текстур для игровых миров — ключ к погружению. В этой статье мы разберем, как с помощью объектов клеточного шума (Cellular Noise) в Phaser генерировать и анимировать сложные паттерны в реальном времени, имитируя природные материалы, магические эффекты или футуристические интерфейсы. Вы научитесь управлять 2D, 3D и 4D шумом, создавая бесконечную, плавно меняющуюся текстуру без использования тяжеловесных спрайтов или видео.

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

Живой запуск

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

Исходный код


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

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

        this.noise1 = this.add.noisecell2d({ noiseCells: [ 5, 9] }, width * 1 / 6, height / 2, width / 3, height);
        this.noise2 = this.add.noisecell3d({ noiseCells: [ 5, 9, 1 ] }, width * 3 / 6, height / 2, width / 3, height);
        this.noise3 = this.add.noisecell4d({ noiseCells: [ 5, 9, 1, 1 ] }, width * 5 / 6, height / 2, width / 3, height);

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

    update (time)
    {
        const t = time / 1000;

        // Circular offset.
        this.noise1.noiseOffset[0] = Math.cos(t);
        this.noise1.noiseOffset[1] = Math.sin(t);

        // Linear offset.
        // Note that precision will eventually decay - you should loop the input.
        this.noise2.noiseOffset[0] = t;

        // Multi-dimensional offset.
        this.noise3.noiseOffset[0] = t
        this.noise3.noiseOffset[1] = Math.sin(t);
        this.noise3.noiseOffset[2] = t
        this.noise3.noiseOffset[3] = Math.sin(t);
    }
}

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

const game = new Phaser.Game(config);

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

В методе create() сцены создаются три визуальных объекта, представляющих разные измерения клеточного шума. Каждый объект размещается по горизонтали на своей трети экрана.

const { width, height } = this.scale;

this.noise1 = this.add.noisecell2d({ noiseCells: [ 5, 9] }, width * 1 / 6, height / 2, width / 3, height);
this.noise2 = this.add.noisecell3d({ noiseCells: [ 5, 9, 1 ] }, width * 3 / 6, height / 2, width / 3, height);
this.noise3 = this.add.noisecell4d({ noiseCells: [ 5, 9, 1, 1 ] }, width * 5 / 6, height / 2, width / 3, height);
*   `this.add.noisecell2d/3d/4d` — фабричные методы для создания объектов шума. Число указывает на количество измерений, используемых при генерации паттерна. Больше измерений — сложнее и «глубже» результат.
*   Параметр `noiseCells` — массив, определяющий базовую структуру ячеек. Значения `[5, 9]` для 2D шума задают количество "точек интереса" в паттерне, влияя на его зернистость и детализацию.
*   Следующие аргументы — это X, Y координаты центра объекта, а затем его ширина и высота.
*   Текстовые метки создаются с помощью `this.add.text` для визуального разделения примеров на экране.

Сердце анимации: метод update и массив noiseOffset

Динамика достигается в методе update(time), который вызывается каждый кадр. Ключевой инструмент — массив noiseOffset у каждого объекта шума. Смещая координаты в пространстве шума, мы создаем иллюзию движения самого паттерна.

update (time)
{
    const t = time / 1000;

    // Circular offset.
    this.noise1.noiseOffset[0] = Math.cos(t);
    this.noise1.noiseOffset[1] = Math.sin(t);

    // Linear offset.
    this.noise2.noiseOffset[0] = t;

    // Multi-dimensional offset.
    this.noise3.noiseOffset[0] = t
    this.noise3.noiseOffset[1] = Math.sin(t);
    this.noise3.noiseOffset[2] = t
    this.noise3.noiseOffset[3] = Math.sin(t);
}

* time — время в миллисекундах с момента старта сцены. Делим на 1000, чтобы получить время `t` в секундах для плавных тригонометрических функций. * this.noise1.noiseOffset[0] и [1] — управляют смещением по осям X и Y в 2D-пространстве шума. Использование Math.cos(t) и Math.sin(t) создает циклическое, круговое движение текстуры. * Для 3D-шума (noise2) смещается только первое измерение ([0] = t), что дает эффект однонаправленного "прокручивания". * 4D-шум (noise3) позволяет анимировать смещение по всем четырем измерениям, создавая наиболее сложную и органичную анимацию, где паттерн трансформируется по нескольким осям одновременно.

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

Клеточный шум — это не просто техническая демонстрация.

* **Фоны планет и ландшафтов:** Анимированный 2D/3D шум может изображать движущиеся облака в атмосфере газового гиганта, течения в океане или мерцающую поверхность звезды. * **Магические и технологические эффекты:** Используйте 4D шум с цветовыми фильтрами (setTint) для создания поля энергии, щита или магического портала. Изменение параметра noiseCells на лету может имитировать нарастание или затухание силы. * **Динамические текстуры объектов:** Назначайте объект шума как текстуру (setTexture) для спрайта, представляющего, например, кипящую жидкость в колбе или поврежденную броню.

// Пример: применение шума как текстуры к спрайту
let magicSphere = this.add.sprite(400, 300, '__WHITE');
magicSphere.setDisplaySize(200, 200);
// Предположим, noiseTexture — это рендер-текстура с нашим шумом
magicSphere.setTexture(noiseTexture);
magicSphere.setTint(0x00ffaa);

Оптимизация и важные замечания

Работа с шумом в реальном времени требует внимания к деталям.

1. **Точность и зацикливание:** Как отмечено в комментарии к 3D-примеру, при линейном увеличении смещения (noiseOffset[0] = t) точность числа с плавающей запятой со временем будет падать. Решение — реализовать логику зацикливания значения, когда оно превышает разумный предел (например, 1000), чтобы избежать артефактов при долгой работе игры. 2. **Производительность:** 4D шум — самый затратный. Используйте его точечно для ключевых визуальных эффектов. Для больших фоновых областей может быть достаточно 2D или 3D шума. 3. **Эксперименты с параметрами:** Массив noiseCells — мощный инструмент. Попробуйте значения [2, 3] для крупных, редких ячеек или [15, 20] для мелкой, почти однородной "песчаной" текстуры. Эффект меняется кардинально.

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

Объекты клеточного шума в Phaser открывают простой путь к созданию уникальных, производительных и бесшовно анимированных визуальных эффектов прямо в Canvas/WebGL. Вы можете управлять их сложностью, поведением и цветом, интегрируя в игровые миры как фон, так и интерфейсные элементы. Для экспериментов попробуйте: связать скорость noiseOffset с игровыми событиями (скорость корабля, уровень маны), комбинировать несколько слоев шума с разными режимами наложения (setBlendMode) или использовать шум как карту высот для простого vertex-смещения.