GameDev 3.0. Этап 3.2. Ассеты. Урок 13. PCG, ч.1
Стартовая страница
- Что такое PCG и для чего оно
- Демонстрация работы PCG
- Как включить PCG в своем проекте
- Основы РCG: bounds volume, Input/Output, Cleanup и Generate
- Как работает Surface Sampler, как включить Debug и для чего он полезен в работе
- Нода Transform Points
- Нода Projection
- Ноды Density Filter и Density Noise, как разделять ветви в PCG
- Нода Static Mesh Spawner
- РСG система на основе сплайнов, BP Spline для контроля PCG
- Контроль плотности точек
- Второй ВP Spline для создания тропинок
- Доработка PCG, нода Grid
- Ray Cast для того, чтобы генерировать объекты поверх других статик мешей
- Что мы затронем в следующем уроке
Что такое PCG и для чего оно
Технология процедурной генерации контента (PCG) в Unreal Engine 5.2 позволяет определять правила и параметры для заполнения больших сцен по выбору пользователя. Это делает процесс создания больших миров быстрым и эффективным.
- внутри игры или другого приложения в реальном времени, чтобы мир мог реагировать на изменения игрового процесса или геометрии;
- для линейного контента, требующего значительного количества ресурсов, например, крупных архитектурных проектов или сцен из фильмов
Работает в версиях начиная с 5.2 и выше.
PCG позволит расставлять ассеты, фольяж, мелкие детали и т.д. намного быстрее и эффективнее и даст возможность редактировать некоторые значения для того, чтобы получить нужный результат в кратчайшие сроки.
↑ Вверх
Демонстрация работы PCG
Можно почитать/посмотреть здесь.
↑ Вверх
Как включить PCG в своем проекте
Заходим в Edit -> Plugins и в поиске пишем procedural:
Ставим галочку, UE попросит перезагрузить проект, перезагружаем и все готово к работе.
↑ Вверх
Основы РCG: bounds volume, Input/Output, Cleanup и Generate
Создаем папку PCG, заходим в нее, нажимаем ПКМ -> PCG -> PCG Graph:
Назовем PCG_spawnmesh. Сохраняем проект, открываем PCG и видим, что он схож с блюпринтами:
По умолчанию в PCG нам даны ноды Input и Output. Нода Output используется, если мы хотим передать данные, которые мы здесь написали в другой PCG graph, но в данном уроке мы не будем использовать Output, так как все системы будут написаны в одном PCG graph для более удобной и быстрой работы.
Так же есть нода Input, в которой находятся наши данные ландшафта по умолчанию.
В PCG есть несколько способов генерировать точки на поверхности. Но самый распространенный и тот, который дается по умолчанию, это Landscape.
Если мы вынесем наш PCG во вьюпорт. То мы увидим, что это большой куб:
Вся генерация будет происходить внутри этого куба.
Есть 2 кнопки, которыми мы будем пользоваться чаще, это Generate и Cleanup:
Cleanup - удаляет процедурную генерацию, которую сделали в уровне.
Эти функции особо полезны, когда мы вносим большие изменения в PCG Graph.
↑ Вверх
Как работает Surface Sampler, как включить Debug и для чего он полезен в работе
Вернемся к PCG Graph и нажмем ПКМ, пишем Surface Sampler, с этой нодой будем работать чаще всего:
Данная нода берет данные поверхности, которые мы ей даем, будь это ландшафт, актор, статик мэш или несколько статик мэшей, и спамит на ней точки, которые далее будут использоваться, чтобы спамить на ней объекты.
Bounding Shape используется для того, чтобы придавать кастомную форму нашему PCG Graph. Чаще всего это будет сплайн, который можно редактировать.
Подключим Landscape к Surface и сохраним:
Вернемся в уровень и нажмем Generate, но мы ничего не увидим:
На самом деле все точки были уже сгенерены, но они просто данные. Чтобы видеть визуально что мы делаем, мы можем зайти в PCG_spawnmesh, выделить ноду Surface Sampler и нажать клавишу D или ПКМ -> Debug и появится голубой кружок в углу нашей ноды:
Перейдя во вьюпорт мы видим, что по умолчанию точки генерятся с рандомным значением плотности:
Плотность можно контролировать другими нодами, с которыми ознакомимся чуть позже. Если вернуться в PCG_spawnmesh, выделить ноду Surface Sampler, то справа в Details увидим, параметры, которые можно поменять и которые влияют на генерацию:
Points per Squared Meter отвечает за то, что сколько точек на квадратный метр могут появляться. Point Extents - какой диапазон у каждой точки. Если сделаем диапазон меньше, например 10х10х10, вернемся в уровень, то увидим, что точки стали меньше и пространство, которое они занимают, тоже:
Если вернемся обратно и в Points per Squared Meter поставим 10, то увидим, что точек стало намного больше:
Ну а пока вернем все на изначальные данные и нажимаем сохранить.
Также Surface Sampler дает нам включить ноду Unbounded, что убирает лимит в квадрат и берет в учет весь ландшафт, который представлен в этом уровне. Но стоит быть внимательным, т.к. многие системы не выдержат такой нагрузки при просчете. Поэтому мы не будем использовать Unbounded и будем использовать либо квадратный Bounded Volume, или кастомные сплайны в дальнейшем.
↑ Вверх
Нода Transform Points
Вторая нода, с которой будем работать - это Transform Points:
Эта нода позволяет нам редактировать то, где эти точки спамятся - выше, ниже, левей, правей, каков макс/мин поворот и размер:
Соединим Surface Sampler -> Out с Transform Points -> In и отключим Debug:
Перейдем на Transform Points и включим Debug здесь. Сохраним и видим, что ничего не поменялось:
А если, например, Rotation Max по Z поставим 360, а Scale Max поставим 3:
то видим, что некоторые точки стали в разы больше, а некоторые остались такими же мелкими и их поворот стал рандомный:
↑ Вверх
Нода Projection
Следующая полезная нода, которой будем пользоваться часто - Projection:
Без этой ноды, например, если некоторые точки подняты максимально вверх, то они не соприкасаются с поверхностью, а висят в воздухе, что неправильно:
Projection берет данные поверхности и распределяет наши точки по этой поверхности. Соединим Transform Points -> Out с Projection -> In, а Input -> Landscape соединим с Projection -> Projection Target:
И после этого все точки соприкасаются с поверхностью:
↑ Вверх
Ноды Density Filter и Density Noise, как разделять ветви в PCG
Следующая нода, которую мы будем рассматривать, это Density Filter:
Density Filter берет минимальное и максимальное значение плотности каждой точки и исключает те, которые не попадают в этот диапазон:
Подключим Density Filter -> In к Projection -> Out, включим Debug:
Теперь мы видим, что темные точки исчезли, так как мы поставили, что в Density Filter все, что меньше 0,5 и больше 1,0 не должны появляться:
Это особенно полезно, если мы хотим спамить несколько видов объектов одним PCG. Например, прокопируем Density Filter и поменяем там значения на 0 и 0,5:
Теперь мы видим, что появились и черные точки:
Ветви PCG Craph будут находиться на отдельных ветках, давая нам возможность спавнить разные меши на каждой из этих веток.
Также можем поставить ноду Density Noise:
Подключим к Density Filter и включим на ней Debug:
По сути наши предыдущие точки вновь могут иметь разнообразие, появляются и черные:
Также в Density Noise можем настроить минимальный и максимальный диапазон:
И у нас теперь точки варьируются от 0,5 до 1:
↑ Вверх
Нода Static Mesh Spawner
Следующая нода - Static Mesh Spawner:
Эта нода берет все наши данные в PCG и используя финальный Out точек, спамит там мэши. Подключим Static Mesh Spawner и здесь можем добавить все Static Mesh, которые мы хотим спавнить:
Открываем настройки Static Mesh Spawner, нажимаем +, открываем список и здесь в Static Mesh выбираем то, что нам нужно:
Видим, что деревья слишком большие, вернувшись в Transform Points сделаем их размер меньше, например так:
Теперь в этот Static Mesh Spawner мы можем добавить несколько разных мэшей, которые будут спавниться в этих точках.
Добавим, например, деревья поменьше:
Можно добавлять мэши безгранично, столько, сколько вам надо.
По нижней ветке, которая спавнит черные точки, мы заспавним ветки, листья и т.д. Добавим ветку:
Если масштаб будет отличаться (слишком большой или наоборот слишком маленький), то добавим Transform Points вот так:
И уже там будем менять масштаб и поворот объектов:
Для более точной настройки спавна добавим еще ноды Density Noise и Density Filter:
Это все ноды, необходимые для PCG, желательно, чтобы эти ноды были общими:
При этом Transform Points можно исключить отсюда и добавлять для каждой ветви отдельно. От этого система не поломается.
↑ Вверх
РСG система на основе сплайнов, BP Spline для контроля PCG
Теперь рассмотрим РСG систему на основе сплайнов. Для этого создаем PCG Graph, назовем ее PCG_Spline:
И откроем ее. Здесь нам не понадобятся ноды Input и Output, вместо них вызовем ноду Get Spline Data:
В Get Spline Data есть несколько акторов, среди которых можно выбрать, но нам нужен All World Actors:
И мы будем выбирать Actor Selection - By Class:
Создадим новый блюпринт класс: ПКМ -> Blueprint Class -> Actor и назовем BP_Spline:
И откроем BP_Spline, добавим сплайн Add -> Spline, не Spline Mesh, а именно Spline:
Сохраняем, переходим в PCG_Spline и в Actor Selection Class выбираем наш BP_Spline:
Сохраняем. Для того, чтобы получать информацию, которую нам выдает Get Spline Data, мы вызываем из Out ноду Spline Sampler:
Эта нода работает так же, как и Surface Sampler, но берет данные, которые находятся на/в/внутри и снаружи сплайна, в зависимости от того, как мы это настроим.
Вернемся в BP_Spline и добавим PCG компонент:
Выделяем PCG, переходим в Details -> Graph и выбираем PCG_Spline:
Теперь Get Spline Data будет получать сплайн дату из нашего блюпринта.
Это работает следующим образом: сплайн закидываем во вьюпорт, с помощью Alt и мышки добавляем точки к сплайну:
Если в Spline ставим галочку в Closed Loop, то сплайн закроется:
И здесь можем выбрать PCG компонент и нажмем Generate:
Но визуально ничего не видим, но если вернемся в PCG_Spline, выберем Spline Sampler и нажмем клавишу D (или ПКМ -> Debug):
то мы увидим, что по сплайну начали появляться вот такие сегменты:
У Spline Sampler есть 2 режима - Subdivision и Distance. Subdivision распределяет сам сколько точек и сегментов в сплайне, а в Distance можно указать самому расстояние, на котором будут появляться эти элементы:
Чем меньше эта дистанция, тем больше сегментов будут появляться:
И соответственно, чем больше дистанция, тем меньше сегментов:
В Spline Sampler в Dimension выберем On Interior:
Теперь мы видим, что форма появляется внутри:
Если поменяем расположение точек, то увидим, что форма меняется:
Чтобы видеть эти точки явней, добавим Transform Points и сделаем Scale Min и Scale Max по 0,4 пока что:
Сохраним и теперь видим эти точки намного лучше:
Если обратить внимание, то мы видим, что точки следуют нашему сплайну, то есть находятся на одном уровне с ним. Если надо, чтобы точки спавнились на ландшафте, то добавим Projection, но в Projection Target подключаем Input -> Landscape:
И теперь точки находятся всегда на поверхности, не смотря на то, где, на какой высоте находится сам сплайн:
После Projection можем добавить Transform Points и поменяем Scale Min на 0,5, а Scale Max на 0,8:
Казалось бы, уже есть Transform Points, но первый отвечает за значение по умолчанию, ну а во втором меняем значения относительно первого:
Можем еще уменьшить размер, а также поставить на Rotation Max по оси Z значение 360 градусов:
↑ Вверх
Контроль плотности точек
Если хотим сделать небольшой сгусток фольяжа и чтобы все это выглядело натурально, нам надо сделать так, чтобы в центре этой формы была густая растительность, а по краям практически нет.
Для этого зайдем в PCG_Spline и в Spline Sampler -> Interior Density Falloff Curve правой кнопкой мыши добавим ключ:
Параметры ключа поставим такие значения - Time = 0, Value = 1:
Добавим еще 1 ключ со значениями Time = 1, Value = 0:
Сохраним и теперь мы видим, что в центре белые точки, то есть их плотность высокая, а по краям - черные точки:
↑ Вверх
Второй ВP Spline для создания тропинок
Теперь создадим второй сплайн, который будет прорезать путь через первый сплайн. К примеру, мы генерим кусочек леса и нам нужно сделать тропинку через лес.
Для этого создадим блюпринт класс - актор BP_SplinePath. Сохраним, зайдем в него и добавим только сплайн:
Теперь перейдем в PCG_Spline и прокопируем ноду Get Spline Data:
В Actor Selection Class выберем BP_SplinePath:
И у нас выйдет ошибка, потому что в уровне нигде не видит такой блюпринт. Если закинем его в уровень, сохраним все, нажмем Forse Regen, то ошибка уйдет.
Далее из Out вызовем так же Spline Sampler, здесь оставим Subdivision и On Spline:
Далее вызовем ноду Bounds Modifier, она изменяет размер точек и их коллизии, которые спавнятся на объекте, в данном случае на сплайне. Bounds Min и Bounds Max по оси Y поставим 100, по оси Z - 200:
Поставим Debug на Bounds Modifier и видим, что у нашего сплайна пути появилась коробочка:
Чтобы видеть путь, заходим в BP_SplinePath, выделяем Spline и в Details находим Scale Visualization Width, ставим галочку и пишем значение 100, так как ранее в Bounds Modifier в Bounds Min и Bounds Max по оси Y мы указали 100:
Видим, что ширина одинаковая. Если мы увеличим в одной точке масштаб, то точки заполнят пространство:
Теперь проложим путь сквозь тот сплайн с лесом, но ничего не произойдет, потому что нам нужно прописать логику, чтобы точки тропинки BP_SplinePath удаляли точки леса/джунглей BP_Spline:
Для этого в BP_Spline мы вызываем ноду Difference, в Source мы подключим верхнюю ветку, а в Differences - нижнюю ветку:
Нода Difference отнимает одни точки от других. Есть несколько разных способов Density Function, но в данном случае нам надо Binary:
Сохраним, выключим Debug на остальных нодах и включим на ноде Difference :
И видим, что там, где идет наша тропинка, точки были удалены. Но допустим, если хотим создать вторую тропинку и сгенерируем, то видим, что ничего не происходит:
Для этого переходим в BP_Spline, выбираем Get Spline Data и в настройках включаем Select Multiple (ставим галочку), во втором Get Spline Data тоже включаем эту функцию:
И теперь видим, что вторая тропинка тоже убирает точки:
Используя такие системы можно работать со сплайнами, а также можно комбинировать сплайны и то, что добавляли в начале урока - PCG_Spawnmesh.
↑ Вверх
Доработка PCG, нода Grid
Сейчас рассмотрим создание точек вокруг существующих точек. Это полезно, если мы хотим, например, генерить большие камни и мелкие камни, разбросанные вокруг них. Или дерево, а вокруг разбросанные мелкие деревья, листья, ветки.
За основу возьмем PCG_Spawnmesh, который создавали ранее, откроем его. Здесь видим предыдущую настроенную систему, отключим пока Static Mesh Spawner:
Отключаются нажатием на клавишу E, либо ПКМ -> Enable и снимем галочку.
Закинем в уровень PCG_Spawnmesh, нажмем Generate.
Для начала подключим еще один Surface Sampler и включим на ней Debug:
Далее подключим Density Filter для того, чтобы уменьшить количество точек и отфильтровать ненужное, включим на ней Debug, в Lower Bound ставим 0,8, а в Upper Bound ставим 1:
Теперь мы видим, что точек стало меньше:
Возможно даже поменяем диапазон от 0,6 до 0,8:
Теперь добавим Transform Points и установим такие значения:
Можно еще "поиграть" данными и может придем к лучшему варианту.
Вернемся в PCG_Spawnmesh, создадим ноду Create Points Grid, включим ей Debug, переключим Scale Method на Relative, Point Scale поставим 0,5, и ставим на Local галочку, сохраним:
Добавим ноду Copy Points, к Source подключим Create Points Grid, а к Target - Transform Points, включим Debug на Transform Points и Copy Points:
Сохраняем, но ничего не видно:
Вызываем ноду Bounds Modifier, подключим ее между нодами Create Points Grid и Copy Points, в Bounds Min и Bounds Max поставим значение по 10:
В Create Points Grid в Local Inverse Transform поставим галочку:
И теперь видим такую картину, появились гриды вокруг наших основных точек:
Но это выглядит слишком идеально и аккуратно, сейчас мы это подправим. В PCG_Spawnmesh после Copy Points добавим Transform Points и в значения Offset Min по X и Y поставим -100, а в Offset Max поставим 100, ось Z не трогаем, включим Debug:
И теперь видим, что они разбросаны:
Так же дадим рандомности в повороте и масштабе:
И теперь это выглядит более натурально и разбросано:
Можно еще попробовать поменять значения, пока не придем к лучшему варианту.
Добавим еще Projection, Projection Target подключим с Input -> Landscape:
И теперь все точки у нас будут ровно на поверхности, на ландшафте:
Не смотря на то, что мы дали рандомизацию, все равно они выглядят как квадраты, чтобы улучшить это, добавим ноду Distance, Source соединим с Projection -> Out, а Target - c Transform Points -> Out. Set Density ставим галочку (включим), Maximum Distance ставим 500:
И мы видим, что есть некая градация:
Далее подключим Density Filter, Lower Bound ставим значение 0, а Upper Bound - 0.6 и включим Debug на этой ноде:
И видим, что точки отфильтровались:
Добавим ноду Execute Blueprint, включим Debug, Blueprint Element Type выберем ScaleByDensity, Scale Min укажем значение 1.0, а Scale Max - 0.4:
Теперь видим, что точки ближе к центральной точке стали больше, а дальние поменьше:
Можно еще поиграть с этими значениями и посмотреть, как будет лучше.
Вот, например, при значениях 1.5 и 0.2 выглядит так:
Теперь добавим ноды Static Mesh Spawner, где 1 - для крупного камня/дерева/кустарника и т.п., а 2 - мелкие камушки/трава и т.д.:
Мы также можем еще дальше разветвляться для деревьев и других объектов:
Эта система также отлично работает для генерации мелких объектов, кустиков, деревьев, насыпей и т.д.
↑ Вверх
Ray Cast для того, чтобы генерировать объекты поверх других статик мешей
Если мы хотим, чтобы это генерилось на статичных объектах, которые мы ставим в уровень, мы можем вызвать ноду World Ray Hit Query:
Подключим ее вместо Input к Surface, обязательно отключаем Filtering -> Ignore Landscape Hits:
И видим, что точки спавнятся поверх статик мешей:
↑ Вверх
Что мы затронем в следующем уроке
Используя этот пресет и придерживаясь такого формата работы, мы сможем создавать системы, которые нам подходят под нашу задачу.
На следующем уроке создадим блюпринт-класс, который может генерить объекты поверх других статик мэшей, раскидать по всей локации и т.д.