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

Генерация процедурного контента — ключевой навык в геймдеве. Шум Перлина и симплекс-шум часто используются для создания текстур, ландшафтов, облаков и других органичных форм. В Phaser есть встроенный Game Object `noisesimplex2d`, который визуализирует 2D симплекс-шум. Один из самых мощных параметров этого объекта — `noiseIterations` (количество итераций). Изменяя его, вы контролируете уровень детализации и сложность получаемой карты шума. Эта статья на практическом примере покажет, как `noiseIterations` превращает простой узор в богатую, многослойную текстуру, готовую для использования в вашей игре.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    create()
    {
        const { width, height } = this.scale;

        this.add.noisesimplex2d({
            noiseCells: [ 4, 9 ]
        }, width * 1 / 8, height / 2, width / 4, height);
        this.add.noisesimplex2d({
            noiseCells: [ 4, 9 ],
            noiseIterations: 2
        }, width * 3 / 8, height / 2, width / 4, height);
        this.add.noisesimplex2d({
            noiseCells: [ 4, 9 ],
            noiseIterations: 3
        }, width * 5 / 8, height / 2, width / 4, height);
        this.add.noisesimplex2d({
            noiseCells: [ 4, 9 ],
            noiseIterations: 4
        }, width * 7 / 8, height / 2, width / 4, height);

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

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

const game = new Phaser.Game(config);

Базовое создание симплекс-шума

В Phaser для добавления 2D симплекс-шума на сцену используется метод this.add.noisesimplex2d(). Этот метод принимает объект конфигурации и параметры позиционирования.

Основные параметры конфигурации из нашего примера: * noiseCells: определяет "крупность" базового узора. Это массив из двух чисел, например [4, 9]. * noiseIterations: количество накладываемых друг на друга слоёв (октав) шума. Чем больше итераций, тем детальнее результат.

Параметры позиционирования (x, y, width, height) задают положение и размер визуализируемого прямоугольника шума на экране.

Вот как создаётся самый простой шум с одной итерацией:

this.add.noisesimplex2d({
    noiseCells: [ 4, 9 ]
}, width * 1 / 8, height / 2, width / 4, height);

Магия параметра noiseIterations

Параметр noiseIterations управляет процессом фрактального шума. По умолчанию он равен 1, что даёт простой, плавный градиент.

Когда вы устанавливаете noiseIterations: 2, система генерирует второй слой шума. Этот слой имеет вдвое большую частоту (более мелкие детали) и вдвое меньшую амплитуду (вносит меньший вклад в итоговое значение). Затем он накладывается на первый слой.

Каждая последующая итерация добавляет новый слой с ещё более высокой частотой и ещё меньшей амплитудой. Это создаёт эффект накопления деталей — от крупных форм к мелким.

Код ниже создаёт четыре визуализации с разным количеством итераций, располагая их рядом для сравнения:

this.add.noisesimplex2d({ noiseCells: [ 4, 9 ] }, width * 1 / 8, height / 2, width / 4, height);
this.add.noisesimplex2d({ noiseCells: [ 4, 9 ], noiseIterations: 2 }, width * 3 / 8, height / 2, width / 4, height);
this.add.noisesimplex2d({ noiseCells: [ 4, 9 ], noiseIterations: 3 }, width * 5 / 8, height / 2, width / 4, height);
this.add.noisesimplex2d({ noiseCells: [ 4, 9 ], noiseIterations: 4 }, width * 7 / 8, height / 2, width / 4, height);

Визуализация и подписи

Чтобы сделать пример наглядным, рядом с каждой визуализацией шума добавляется текстовая метка. Для создания текста используется this.add.text().

Метод принимает координаты (x, y), строку с текстом и объект стиля. В примере задан только fontSize. После создания текстового объекта к нему применяются методы для улучшения визуального вида: setStroke() добавляет контур, а setShadow() — тень, что делает текст читаемым на любом фоне.

Расположение текста жёстко привязано к долям ширины экрана (width / 4 + 10, width / 2 + 10 и т.д.), что обеспечивает его корректное отображение рядом с соответствующим блоком шума.

this.add.text(10, 10, '1 iteration', { fontSize: 24 })
    .setStroke('#ff8844', 2)
    .setShadow(2, 2, '#333333', 2, true, false);

Конфигурация игры и сцены

Пример построен по классической для Phaser схеме. Создаётся класс сцены Example, который расширяет Phaser.Scene. Вся логика отрисовки находится в его методе create().

Затем определяется объект config — конфигурация игры. Ключевые параметры: * type: Phaser.WEBGL: использует WebGL-рендерер, который необходим для отрисовки шума. * parent: ID HTML-элемента, в который будет встроен canvas. * width и height: размеры canvas. * scene: класс, который будет использоваться в качестве основной сцены.

Финальный шаг — инстанциирование игры с этой конфигурацией.

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

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

Параметр noiseIterations — это мощный рычаг для управления сложностью процедурного контента. Всего несколько строк кода позволяют получить спектр текстур — от плавных градиентов для фона до детализированных карт высот для террейна. Идеи для экспериментов: 1. Свяжите значение noiseIterations со временем или действиями игрока для анимированного "проявления" деталей в текстуре. 2. Используйте шум с большим количеством итераций (5-6) в качестве карты высот для генерации островов с реалистичной береговой линией. 3. Поэкспериментируйте с другими параметрами noisesimplex2d, например, noiseScale или colors, чтобы создавать разноцветные облака, лаву или магические поля.