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

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

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    cloudLayers = [];

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

        this.add.gradient({
            bands: [
                {
                    colorStart: 0x2244aa,
                    colorEnd: 0x88aaff
                }
            ],
            start: { x: 0, y: 1 },
            shape: { x: 0, y: -1 }
        }, width / 2, height / 2, width, height);

        const addCloudLayer = (k) => {
            const c = 1 - k * k * 0.3;
            const noise = this.add.noisesimplex3d({
                noiseOffset: [ 0, 0, k / 4 ], // Depth evolution
                noiseCells: [ 4, 4, 4 ],
                noiseIterations: 4, // Nice level of detail.
                noiseColorStart: [ c, c, c, 0 ],
                noiseColorEnd: [ c, c, 1, 0.2 ], // Tint shadows blue.
                noiseValueAdd: 0.1 // Increase open areas.
            }, width / 2, height / 2 + k * 64, width / 4, width / 4)
            .setScale(5 - k / 1.7, (5 - k) / 1.7); // Simulate parallax.

            this.cloudLayers.push(noise);
        };

        // Add several layers of clouds.
        // Adjust the Z offset for every layer, so the clouds change with height.
        // This causes a lot of overdraw; you'd probably want to pre-render it in production.
        for (let i = 0; i <= 1; i += 0.05)
        {
            addCloudLayer(i);
        }
    }

    update (time)
    {
        for (const cloudLayer of this.cloudLayers)
        {
            cloudLayer.noiseOffset[0] = -time / 5000;
            cloudLayer.noiseOffset[1] = -time / 15000;
        }
    }
}

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

const game = new Phaser.Game(config);

Подготовка сцены и фона

Первым делом в методе create() мы создаем простой градиентный фон, который будет имитировать небо. Это задает базовый цветовой тон для всей сцены.

this.add.gradient({
    bands: [
        {
            colorStart: 0x2244aa,
            colorEnd: 0x88aaff
        }
    ],
    start: { x: 0, y: 1 },
    shape: { x: 0, y: -1 }
}, width / 2, height / 2, width, height);

Здесь используется this.add.gradient(). Объект конфигурации определяет одну цветовую полосу (bands) от темно-синего (0x2244aa) к светло-голубому (0x88aaff). Параметры start и shape задают направление градиента снизу вверх. Градиент позиционируется по центру и растягивается на всю ширину и высоту игры.

Функция создания слоя облаков

Основная магия происходит в функции addCloudLayer(k). Параметр `k` (от 0 до 1) определяет "глубину" слоя. Эта функция создает и настраивает один слой шума, который будет выглядеть как облака.

const addCloudLayer = (k) => {
    const c = 1 - k * k * 0.3;
    const noise = this.add.noisesimplex3d({
        noiseOffset: [ 0, 0, k / 4 ],
        noiseCells: [ 4, 4, 4 ],
        noiseIterations: 4,
        noiseColorStart: [ c, c, c, 0 ],
        noiseColorEnd: [ c, c, 1, 0.2 ],
        noiseValueAdd: 0.1
    }, width / 2, height / 2 + k * 64, width / 4, width / 4)
    .setScale(5 - k / 1.7, (5 - k) / 1.7);

    this.cloudLayers.push(noise);
};

Давайте разберем ключевые параметры объекта NoiseSimplex3D: * noiseOffset: Смещение в 3D-пространстве шума. Z-компонента (k / 4) уникальна для каждого слоя, что обеспечивает разную текстуру. * noiseCells и noiseIterations: Контролируют детализацию и сложность паттерна. * noiseColorStart и noiseColorEnd: Задают градиент цвета для шума. Здесь используется расчетная переменная `c, чтобы слои на разной глубине имели разную яркость, а синий оттенок ([c, c, 1, 0.2]`) добавляет холодных теней. * noiseValueAdd: Повышает базовое значение шума, делая "пробелы" (небо) более светлыми и прозрачными.

Метод .setScale(5 - k / 1.7, (5 - k) / 1.7) создает эффект параллакса: дальние слои (с большим `k) масштабируются меньше, чем ближние, что усиливает ощущение глубины. Созданный объект сохраняется в массивthis.cloudLayers` для последующей анимации.

Генерация многослойности

Чтобы создать плавный переход и ощущение объема, мы генерируем не один, а множество слоев шума с небольшим шагом.

for (let i = 0; i <= 1; i += 0.05)
{
    addCloudLayer(i);
}

Цикл создает около 20 слоев (`iот 0 до 1 с шагом 0.05). Каждый следующий слой немного смещен вниз (height / 2 + k * 64), имеет уникальное смещение по Z-оси (k / 4`) и свой масштаб. Это создает плотный, объемный облачный покров. Авторы примера честно предупреждают: такой подход вызывает значительную перерисовку (overdraw) и для production-проекта такие слои лучше пререндерить в текстуру.

Анимация в реальном времени

Статичная картинка — это скучно. В методе update(time) мы оживляем облака, заставляя их медленно дрейфовать.

update (time)
{
    for (const cloudLayer of this.cloudLayers)
    {
        cloudLayer.noiseOffset[0] = -time / 5000;
        cloudLayer.noiseOffset[1] = -time / 15000;
    }
}

Здесь мы итерируем по всем сохраненным слоям и изменяем компоненты X и Y их свойства noiseOffset. Время time автоматически передается Phaser'ом. Разные делители (5000 и 15000) задают разную скорость смещения по осям, что делает движение более естественным и сложным. Изменение noiseOffset приводит к пересчету 3D-шума и, как следствие, к плавной деформации формы "облаков".

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

Объект NoiseSimplex3D в Phaser — мощный инструмент для процедурной генерации динамических текстур. Как мы увидели, с его помощью можно создавать сложные многослойные эффекты с параллаксом и анимацией, написав всего несколько десятков строк кода. **Идеи для экспериментов:** 1. Измените noiseColorEnd на [1, c, c, 0.2] для создания закатных или огненных облаков. 2. Поиграйте с параметрами noiseCells и noiseIterations, чтобы получить более крупные или, наоборот, мелкие и детализированные облачные формы. 3. Добавьте интерактивность: привяжите скорость анимации (time / 5000) к силе ветра в вашей игре. 4. Используйте этот же принцип для создания анимированной воды, дыма или магических полей.