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

Создание процедурных текстур — мощный инструмент для любого геймдев-арсенала. В этой статье мы разберем пример из официальной документации Phaser, который демонстрирует работу с объектом Simplex Noise. Вы узнаете, как генерировать и рендерить шум в текстуру, а затем динамически управлять его параметрами, в частности — эффектом "заворачивания" (wrap). Этот прием полезен для создания бесшовных фонов, динамических текстур ландшафта или абстрактных визуальных эффектов прямо во время выполнения игры.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    noise;
    toggle = false;

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

        this.noise = this.add.noisesimplex2d({
            noiseCells: [ 8, 4 ],
            noiseSeed: [ 3, 2 ], // Eliminates some distracting details
            noiseIterations: 4,
            noiseColorStart: 0x446688,
            noiseColorEnd: 0xbbffaa,
        }, width / 4, height / 2, width / 2, height / 2)
        .setRenderToTexture('noise');

        this.add.image(0, 0, 'noise').setOrigin(0);
        this.add.image(0, height / 2, 'noise').setOrigin(0);
        this.add.image(width / 2, 0, 'noise').setOrigin(0);
        this.add.image(width / 2, height / 2, 'noise').setOrigin(0);

        this.add.text(10, 10, 'Click to toggle wrap', { fontSize: 24 }).setStroke('#ff8844', 2).setShadow(2, 2, '#333333', 2, true, false);

        this.input.on('pointerdown', () => {
            this.toggle = !this.toggle;
            if (this.toggle)
            {
                this.noise.noisePeriod = [ 0, 0 ];
            }
            else
            {
                this.noise.noisePeriod = [ 8, 4 ];
            }
        });
    }
}

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

const game = new Phaser.Game(config);

Создание и настройка объекта шума

В методе create() сцены создается основной объект — 2D Simplex Noise. Это не просто изображение, а процедурный генератор, который можно рендерить в текстуру.

Ключевые параметры инициализации: - noiseCells: [8, 4] — задает базовую "крупность" или размер ячейки шума по осям X и Y. - noiseSeed: [3, 2] — начальное значение (сид) для генератора случайных чисел, определяющее конкретный паттерн. Изменение сида кардинально меняет итоговую текстуру. - noiseIterations: 4 — количество итераций наложения шума (октав). Чем больше, тем детальнее и сложнее результат. - noiseColorStart и noiseColorEnd: задают градиент цветов, в который окрашивается сгенерированный шум.

Метод .setRenderToTexture('noise') указывает движку, что результат работы генератора нужно сохранить в текстуру с ключом 'noise'. Эта текстура затем может быть использована как любой другой графический ресурс.

this.noise = this.add.noisesimplex2d({
    noiseCells: [ 8, 4 ],
    noiseSeed: [ 3, 2 ],
    noiseIterations: 4,
    noiseColorStart: 0x446688,
    noiseColorEnd: 0xbbffaa,
}, width / 4, height / 2, width / 2, height / 2)
.setRenderToTexture('noise');

Использование текстуры: один шум, четыре изображения

После того как текстура 'noise' создана, ее можно применять для спрайтов. В примере создается четыре изображения, каждое из которых использует одну и ту же текстуру, но размещается в разных углах экрана. Это наглядно демонстрирует, что текстура генерируется один раз, а затем может быть переиспользована многократно без дополнительных вычислительных затрат.

Метод .setOrigin(0) устанавливает точку привязки (origin) изображения в его левый верхний угол. Это упрощает расчет позиций, так как координаты (0,0) и (width/2, height/2) точно совпадают с углами экрана.

this.add.image(0, 0, 'noise').setOrigin(0);
this.add.image(0, height / 2, 'noise').setOrigin(0);
this.add.image(width / 2, 0, 'noise').setOrigin(0);
this.add.image(width / 2, height / 2, 'noise').setOrigin(0);

Динамическое управление: переключение режима Wrap

Самый интересный аспект примера — возможность менять параметры шума в реальном времени. По клику мыши срабатывает обработчик события pointerdown, который инвертирует логическую переменную toggle.

В зависимости от состояния toggle, мы меняем свойство noisePeriod объекта this.noise: - [8, 4] (значение по умолчанию, соответствует noiseCells): шум ведет себя "обычно". - [0, 0]: активирует режим "заворачивания" (wrap) по обеим осям. При этом паттерн шума становится идеально повторяющимся (тилируемым) без видимых швов.

Именно это переключение и демонстрирует разницу между четырьмя отрисованными плитками. В режиме [0,0] границы между ними становятся незаметными, создавая иллюзию одной большой бесшовной текстуры.

this.input.on('pointerdown', () => {
    this.toggle = !this.toggle;
    if (this.toggle)
    {
        this.noise.noisePeriod = [ 0, 0 ];
    }
    else
    {
        this.noise.noisePeriod = [ 8, 4 ];
    }
});

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

Объект noisesimplex2d в Phaser — это гибкий инструмент для динамической генерации текстур. Умение управлять его свойствами, такими как noisePeriod, открывает двери к созданию бесшовных миров и анимированных фонов. Для экспериментов попробуйте: анимировать изменение noiseSeed для плавной "эволюции" текстуры; использовать несколько слоев шума с разными noiseColorStart/End для сложных материалов; привязать переключение noisePeriod не к клику, а, например, к положению камеры для создания бесконечного прокручивающегося фона.