Дипломная работа: процедурная генерация ландшафта в реальном времени на Unreal Engine 5.1. Код написан в 2023 году и с тех пор не развивался — я оставляю его как есть, потому что он показывает работу с алгоритмами шума, а не мой сегодняшний подход к архитектуре.
Актор AGenerator строит сетку TileCount x TileCount через UProceduralMeshComponent
и задаёт высоту вершин функцией шума. Сетка перестраивается вокруг игрока, когда он
отходит от её центра дальше чем на четверть размера.
Реализованы вручную, без использования FMath::PerlinNoise:
- шум Перлина на таблице перестановок с интерполяцией по кривой
6x^5 - 15x^4 + 10x^3; - двумерный симплекс-шум (скос координат, три угла симплекса, градиенты по хешу);
- фрактальное суммирование октав с настраиваемыми
LacunarityиPersistence; - набор вспомогательных шумов для сравнения: белый, синус-косинус, плоский.
Параметры шума вынесены в FNoiseInfo и настраиваются из редактора,
результат виден сразу через OnConstruction.
Список писал заново, перечитав собственный код:
- Генерация в игровом потоке. Перестроение сетки происходит синхронно в
Tick, отсюда фризы при пересечении границы. Расчёт вершин параллелится тривиально (ParallelForпо строкам), готовка коллизии выносится черезbUseAsyncCooking. - Четырёхкратный пересчёт шума. Цикл идёт по клеткам и считает высоту всех четырёх
углов, хотя углы общие у соседних клеток. Нужно два прохода: сначала вершины и UV
по сетке
(TileCount + 1)^2, потом индексы треугольников. - Нормали. Считается одна нормаль на клетку и присваивается трём вершинам, соседние клетки её перезаписывают — освещение ложится гранями. Правильно накапливать нормали по всем смежным треугольникам и нормализовать в конце. Тангенты не заполняются вообще, поэтому нормал-мапы в материале работать не будут.
- Пересоздание секции меша. Топология не меняется, меняются только позиции вершин,
так что после первого раза достаточно
UpdateMeshSectionвместоCreateMeshSection. Плюс лишнийAttachToComponentпри каждой перегенерации. - Одна плитка вместо чанков. Сейчас это один меш, который телепортируется за игроком. Нормальное решение — сетка чанков с кешем, догрузкой соседей заранее и LOD по расстоянию.
- Архитектура. Вся ответственность в одном акторе: и данные, и генерация, и отрисовка.
Расчёт высот — чистая функция, ему нечего делать в
AActor; я бы вынес его в отдельный класс без зависимости от движка, а актор оставил бы тонким слоем представления. Выбор шума черезswitchи указатель на функцию заменил бы на стратегию. - Детерминированность.
WhiteNoiseиспользуетFMath::RandRange, то есть не воспроизводится от сида. Для мультиплеера или сохранений это ломает всё: сид должен полностью определять результат. - Мелочи стиля UE. Перечисление без префикса
E, поля публичные вместоUPROPERTYс доступом через геттеры, таблица перестановок какint32[512]вместоuint8[512], отладочный вывод в релизном пути.
Unreal Engine 5.1, модуль ProceduralMeshComponent подключается в Build.cs.