О чем этот пример
Визуальные эффекты на основе шума — мощный инструмент для создания динамических фонов, магических полей, искажений пространства и других атмосферных элементов в играх. Phaser предоставляет встроенные объекты для работы с Simplex Noise, которые можно не только отрисовывать, но и анимировать в реальном времени. Эта статья покажет, как создать и анимировать 2D и 3D текстуры шума, используя свойства `noiseWarpAmount`. Вы научитесь генерировать плавно меняющиеся абстрактные паттерны, которые можно использовать как самостоятельные арт-объекты или как маски для других игровых элементов.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
noise2d;
noise3d;
create()
{
const { width, height } = this.scale;
this.noise2d = this.add.noisesimplex2d({
noiseCells: [ 8, 9 ],
noiseIterations: 3,
noiseColorEnd: 0x88ff88
}, width / 4, height / 2, width / 2, height);
this.noise3d = this.add.noisesimplex3d({
noiseCells: [ 8, 9, 4 ],
noiseIterations: 3,
noiseColorEnd: 0x8888ff
}, width * 3 / 4, height / 2, width / 2, height);
this.add.text(10, 10, '2D', { fontSize: 24 }).setStroke('#ff8844', 2).setShadow(2, 2, '#333333', 2, true, false);
this.add.text(width / 2 + 10, 10, '3D', { fontSize: 24 }).setStroke('#ff8844', 2).setShadow(2, 2, '#333333', 2, true, false);
}
update (time)
{
const t = time / 1000;
this.noise2d.noiseWarpAmount = Math.sin(t) * 0.5 + 0.5;
this.noise3d.noiseWarpAmount = Math.sin(t) * 0.5 + 0.5;
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 1280,
height: 720,
scene: Example
};
const game = new Phaser.Game(config);
Инициализация сцены и объектов шума
Класс сцены содержит два ключевых свойства: noise2d и noise3d. Они будут хранить ссылки на наши объекты шума.
В методе create() мы сначала получаем размеры игрового поля через this.scale. Это нужно для корректного позиционирования объектов.
Создание 2D шума происходит с помощью метода this.add.noisesimplex2d(). Первым аргументом передается объект конфигурации, а последующие четыре аргумента задают позицию и размеры объекта (x, y, width, height). Аналогично создается 3D шум через this.add.noisesimplex3d().
create() {
const { width, height } = this.scale;
this.noise2d = this.add.noisesimplex2d({
noiseCells: [ 8, 9 ],
noiseIterations: 3,
noiseColorEnd: 0x88ff88
}, width / 4, height / 2, width / 2, height);
this.noise3d = this.add.noisesimplex3d({
noiseCells: [ 8, 9, 4 ],
noiseIterations: 3,
noiseColorEnd: 0x8888ff
}, width * 3 / 4, height / 2, width / 2, height);
}
Настройка параметров шума: cells, iterations и color
Конфигурационный объект позволяет тонко настроить вид генерируемой текстуры.
- noiseCells: Массив, определяющий количество "ячеек" шума по каждому измерению. Для 2D шума это [ширина, высота], для 3D — [ширина, высота, глубина]. Меньшие значения дают более крупные и плавные волны, большие — мелкую и частую рябь. В примере 2D шум имеет 8x9 ячеек, а 3D — 8x9x4.
- noiseIterations: Количество итераций наложения шума (октав). Чем больше итераций, тем детальнее и сложнее выглядит итоговая текстура. Значение 3 — хороший баланс между производительностью и визуальной насыщенностью.
- noiseColorEnd: Конечный цвет градиента, в который окрашивается текстура шума. Начальный цвет — черный (0x000000). 2D шум окрашивается в зеленоватый оттенок (0x88ff88), а 3D — в синеватый (0x8888ff).
// Конфиг для 2D шума
{
noiseCells: [ 8, 9 ],
noiseIterations: 3,
noiseColorEnd: 0x88ff88
}
Динамическая анимация через noiseWarpAmount
Статичная текстура шума — это лишь половина дела. Настоящая магия начинается, когда мы начинаем её искажать. За это отвечает свойство noiseWarpAmount (сила деформации шума), которое есть у обоих объектов.
В методе update(time), который вызывается каждый кадр, мы используем текущее время (time, в миллисекундах) для создания циклической анимации. Время делится на 1000, чтобы получить значение в секундах, которое затем подается на функцию Math.sin(). Синусоида создает плавное oscillating-движение между -1 и 1.
Чтобы преобразовать этот диапазон в удобные для noiseWarpAmount значения от 0 до 1, мы применяем формулу: Math.sin(t) * 0.5 + 0.5. Таким образом, сила деформации будет плавно нарастать от 0 до 1 и обратно до 0, создавая эффект "дыхания" или пульсации текстуры.
update (time) {
const t = time / 1000;
this.noise2d.noiseWarpAmount = Math.sin(t) * 0.5 + 0.5;
this.noise3d.noiseWarpAmount = Math.sin(t) * 0.5 + 0.5;
}
Создание игрового экземпляра (Game Config)
Финальный шаг — создание экземпляра игры Phaser.Game с конфигурацией. Ключевые моменты:
- type: Phaser.WEBGL: Обязательно используем WebGL-рендерер, так как объекты шума работают только в этом режиме.
- В свойствах width и height задается разрешение игрового поля.
- scene: Example: Указываем наш класс сцены, который будет запущен сразу при старте игры.
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 1280,
height: 720,
scene: Example
};
const game = new Phaser.Game(config);
Что попробовать дальше
Используя всего несколько строчек кода, мы создали две живые, пульсирующие текстуры шума, которые могут служить отличной основой для визуальных эффектов в вашей игре. Главный механизм анимации — циклическое изменение свойства noiseWarpAmount.
Идеи для экспериментов:
1. Привяжите noiseWarpAmount не ко времени, а к игровым событиям (например, силе магии персонажа или уровню помех на радаре).
2. Используйте текстуру шума в качестве маски (setMask) для других объектов, чтобы создавать эффекты растворения или энергетических барьеров.
3. Поэкспериментируйте с параметрами noiseCells и noiseIterations для получения совершенно разных визуальных стилей — от гигантских водяных волн до мелкой металлической крошки.
