О чем этот пример
Создание процедурных текстур, таких как шум, — мощный инструмент для геймдева. Однако часто возникают видимые швы и повторения паттернов, которые разрушают иллюзию. В этом примере рассмотрим, как с помощью параметра `noiseWrap` в `Phaser.GameObjects.NoiseCell2D` легко генерировать бесшовный («зацикленный») клеточный шум. Это особенно полезно для создания текстур для бесконечных или тайловых миров, анимированных фонов и динамических материалов, где важна непрерывность.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
class Example extends Phaser.Scene
{
noise;
create()
{
const { width, height } = this.scale;
// Render a noise texture with internal repetition.
this.noise = this.add.noisecell2d({
noiseCells: [ 8, 4 ],
noiseWrap: [ 2, 2 ] // Wrap every 2 cells in X and Y, relative to noiseCells.
}, 0, 0, 512, 256).setRenderToTexture('noise');
// Render several images to show the texture tiles.
// This happens with any integer cells:wrap ratio.
this.add.image(width / 2 - 256, height / 2 - 128, 'noise');
this.add.image(width / 2 + 256, height / 2 - 128, 'noise').setTint(0xff8844);
this.add.image(width / 2 - 256, height / 2 + 128, 'noise').setTint(0x44ff88);
this.add.image(width / 2, height / 2 + 256, 'noise').setTint(0x8844ff).setOrigin(0,1).setRotation(0.1);
}
update (time)
{
const t = time / 5000;
this.noise.noiseOffset[0] = Math.cos(t);
this.noise.noiseOffset[1] = Math.sin(t);
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 1280,
height: 720,
scene: Example
};
const game = new Phaser.Game(config);
Создание объекта шума с зацикливанием
Ключевой объект — Phaser.GameObjects.NoiseCell2D, создаваемый через фабрику this.add.noisecell2d(). Его конфигурация позволяет контролировать, как шум будет повторяться.
this.noise = this.add.noisecell2d({
noiseCells: [ 8, 4 ],
noiseWrap: [ 2, 2 ]
}, 0, 0, 512, 256).setRenderToTexture('noise');
Параметр noiseCells: [8, 4] задает сетку из 8 клеток по ширине и 4 по высоте, из которых генерируется исходный шумовой паттерн. Параметр noiseWrap: [2, 2] указывает движку, что шум должен «завернуться» (стать бесшовным) каждые 2 клетки по осям X и Y, *относительно размера сетки noiseCells*. Таким образом, общий паттерн текстуры будет состоять из 4 повторяющихся блоков (8/2 = 4 по X, 4/2 = 2 по Y), которые идеально стыкуются друг с другом.
Метод .setRenderToTexture('noise') рендерит сгенерированный шум в текстуру с ключом 'noise', которую можно многократно использовать как обычное изображение.
Визуализация тайлов и проверка бесшовности
Чтобы наглядно убедиться в работе noiseWrap, мы отрисовываем несколько копий текстуры 'noise' рядом и с настройками.
this.add.image(width / 2 - 256, height / 2 - 128, 'noise');
this.add.image(width / 2 + 256, height / 2 - 128, 'noise').setTint(0xff8844);
this.add.image(width / 2 - 256, height / 2 + 128, 'noise').setTint(0x44ff88);
this.add.image(width / 2, height / 2 + 256, 'noise').setTint(0x8844ff).setOrigin(0,1).setRotation(0.1);
Мы размещаем четыре изображения, три из которых смещены относительно центра. Благодаря noiseWrap, края каждой текстуры идеально совпадают с краями соседней, создавая впечатление единого, непрерывного полотна шума, даже если частично накладывать их или вращать. Изменение оттенка (setTint) и точки вращения (setOrigin) помогает визуально отделить копии, не нарушая восприятия бесшовности самого паттерна.
Анимация шума через смещение (offset)
Одним из главных преимуществ процедурного шума является возможность его динамического изменения без загрузки новых ассетов. В методе update мы анимируем шум, плавно смещая его точку отсчета.
update (time)
{
const t = time / 5000;
this.noise.noiseOffset[0] = Math.cos(t);
this.noise.noiseOffset[1] = Math.sin(t);
}
Мы используем глобальное время time для создания циклического значения `t. Затем записываем новые координаты смещения в массивthis.noise.noiseOffset. Индекс[0]отвечает за смещение по оси X,[1]— по оси Y. ИспользованиеMath.cosиMath.sin` обеспечивает плавное круговое движение паттерна.
Важно: изменение свойств объекта noise (таких как noiseOffset) автоматически обновляет связанную с ним текстуру 'noise'. Все изображения, использующие эту текстуру, мгновенно отображают анимированный шум, что очень эффективно.
Настройка конфигурации игры
Для работы шейдерного шума (NoiseCell2D) необходим контекст WebGL. Это задается в основном конфиге игры.
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 1280,
height: 720,
scene: Example
};
Убедитесь, что параметр type установлен в Phaser.WEBGL, а не Phaser.CANVAS. Рендерер Canvas не поддерживает данный тип объектов. Сцена Example, содержащая всю логику, передается в свойство scene.
Что попробовать дальше
Параметр noiseWrap в Phaser.GameObjects.NoiseCell2D — это элегантное решение для создания бесшовных, анимируемых процедурных текстур. Соотношение noiseCells и noiseWrap позволяет гибко управлять размером повторяющегося блока. Для экспериментов попробуйте изменить значения noiseWrap на [4, 2] или [1, 1], чтобы увидеть, как меняется паттерн. Используйте разные математические функции в update для создания эффектов волн, дрожания или плавного дрейфа. Такой шум отлично подойдет для симуляции огня, воды, облаков или статичных полей в вашей игре.
