О чем этот пример
Параллаксные облака, плавно эволюционирующие во времени, могут мгновенно добавить глубину и атмосферу вашей 2D-игре. В этой статье мы разберем пример использования игрового объекта `noisesimplex3d` в Phaser для генерации таких облаков прямо во время выполнения. Вы научитесь создавать многослойные, анимированные небесные просторы с минимальным количеством кода, используя встроенные средства для работы с процедурным шумом.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
cloudLayers = [];
create()
{
const { width, height } = this.scale;
this.add.gradient({
bands: [
{
colorStart: 0xaa4422,
colorEnd: 0xffaa88,
colorSpace: 1
}
],
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.5;
const noise = this.add.noisesimplex3d({
noiseOffset: [ 0, 0, k / 4 ], // Depth evolution
noiseCells: [ 4, 4, 4 ],
noiseIterations: 4, // Nice level of detail.
noiseColorStart: [ 1, 1, c, 0 ], // Tint bright layers gold.
noiseColorEnd: [ 1, c, c, 0.2 ], // Tint shadows red.
noiseValueAdd: 0.1, // Increase open areas.
noiseWarpAmount: 0.1,
noiseWarpIterations: 3
}, 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;
cloudLayer.noiseFlow = time / 4000;
}
}
}
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: 0xaa4422,
colorEnd: 0xffaa88,
colorSpace: 1
}
],
start: { x: 0, y: 1 },
shape: { x: 0, y: -1 }
}, width / 2, height / 2, width, height);
Здесь используется this.add.gradient(). Конфигурация start и shape создает вертикальный градиент, который начинается снизу (y: 1) и идет вверх (y: -1). Цвета подобраны так, чтобы создать теплую, оранжево-красную палитру.
Функция для создания слоя облаков
Основная магия происходит в функции addCloudLayer(k). Параметр `k` (от 0 до 1) определяет "глубину" или высоту слоя облаков. Это ключ к созданию эффекта параллакса.
Сначала мы рассчитываем коэффициент `c` для затемнения цветов нижних слоев, делая их более "тяжелыми" и красноватыми.
const c = 1 - k * k * 0.5;
Затем создается сам объект облаков с помощью this.add.noisesimplex3d(). Это специальный игровой объект Phaser, который визуализирует 3D симплекс-шум.
const noise = this.add.noisesimplex3d({
noiseOffset: [ 0, 0, k / 4 ], // Depth evolution
noiseCells: [ 4, 4, 4 ],
noiseIterations: 4, // Nice level of detail.
noiseColorStart: [ 1, 1, c, 0 ], // Tint bright layers gold.
noiseColorEnd: [ 1, c, c, 0.2 ], // Tint shadows red.
noiseValueAdd: 0.1, // Increase open areas.
noiseWarpAmount: 0.1,
noiseWarpIterations: 3
}, width / 2, height / 2 + k * 64, width / 4, width / 4)
.setScale(5 - k / 1.7, (5 - k) / 1.7); // Simulate parallax.
Разберем ключевые параметры:
- noiseOffset: Смещение в 3D-пространстве шума. Z-компонента (k / 4) уникальна для каждого слоя, что дает разную текстуру облаков на разных высотах.
- noiseCells и noiseIterations: Определяют детализацию и сложность паттерна шума.
- noiseColorStart/End: Задают цветовой градиент для ярких и темных участков шума. Мы используем рассчитанный `c`, чтобы нижние слои были краснее.
- noiseValueAdd: Немного "подсвечивает" шум, увеличивая области без облаков (небо).
- noiseWarpAmount/Iterations: Добавляют искажения (domain warping) для более органичной, не повторяющейся формы.
Метод .setScale() имитирует параллакс: дальние слои (с меньшим `k) масштабируются сильнее, чем ближние, создавая иллюзию глубины. Смещение по Y (height / 2 + k * 64`) немного опускает нижние, "ближние" слои.
Генерация многослойного неба
Чтобы небо не выглядело плоским, мы создаем множество слоев облаков, от дальних до ближних.
for (let i = 0; i <= 1; i += 0.05)
{
addCloudLayer(i);
}
Цикл вызывает функцию addCloudLayer с шагом 0.05, создавая 21 слой. Каждый слой получает свое значение `i, которое влияет на его цвет, текстуру, положение и масштаб. Автор примера честно предупреждает: такой подход вызывает значительныйoverdraw` (перерисовку пикселей), и для production-проекта эти слои лучше пререндерить в текстуру.
Анимация облаков
Статичные облака смотрятся скучно. В методе update(time) мы оживляем все слои, изменяя параметры их шума на основе игрового времени.
for (const cloudLayer of this.cloudLayers)
{
cloudLayer.noiseOffset[0] = -time / 5000;
cloudLayer.noiseOffset[1] = -time / 15000;
cloudLayer.noiseFlow = time / 4000;
}
Мы итерируем по массиву cloudLayers, куда ранее сохраняли все созданные объекты noisesimplex3d.
- Изменение noiseOffset[0] (по X) заставляет облака медленно плыть вправо.
- Изменение noiseOffset[1] (по Y) с другой скоростью добавляет сложное, нелинейное движение.
- Параметр noiseFlow управляет внутренней эволюцией формы шума, создавая эффект "кипящих" или плавно изменяющихся облаков.
Разные делители (5000, 15000, 4000) задают разные скорости для каждого типа движения, что делает анимацию более натуральной.
Что попробовать дальше
Объект noisesimplex3d в Phaser — это мощный инструмент для процедурной генерации динамических фоновых элементов, таких как облака, туман, огонь или вода. В этом примере мы увидели, как комбинация нескольких слоев шума с параллаксом и анимацией создает сложную и живую картину. Для экспериментов попробуйте: изменить палитру в noiseColorStart/End на холодные тона для грозового неба; увеличить noiseWarpAmount для более рваных, грозовых облаков; анимировать noiseCells или noiseIterations для драматического эффекта формирования или рассеивания тумана.
