О чем этот пример
Создание динамического фона с эффектом глубины — частый запрос в играх. Рисовать каждый слой вручную долго и негибко. В этой статье мы разберем пример из официальной документации 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. Используйте этот же принцип для создания анимированной воды, дыма или магических полей.
