Перед мной возникла задача создать демку на Unreal Engine 5 с возможностью динамической генерации навигации для камеры и встроенной поддержкой загрузки файлов в формате USD на уровень в режиме выполнения (отдельная игра без редактора).
Для импорта USD файлов в UE5 доступен плагин USD Importer, который позволяет загружать файлы формата USD путем создания USDStageActor
(далее "актер") - эта функциональность доступна только в редакторе. Однако как указано в документации, есть возможность использования плагина в режиме выполнения, просто установив флаг FORCE_ANSI_ALLOCATOR=1
в Project.Target.cs
. Этот флаг заставляет использовать стандартный аллокатор вместо аллокатора UE5.
Вроде все просто установил флаг и радуйся, но не тут-то было при сборке заругалось: «Project modifies the value of GlobalDefinitions. This is not allowed, as Project has build products in common with UnrealGame». Оказалось, что чтобы использовать GlobalDefinitions нужно собирать движок из исходников.
В официальной документации не нашлось упоминания об этом моменте, а поиск в Google ничего конкретного не принес. Как всегда в таких ситуациях, выручила интуиция.
Сначала для навигации хотел использовать NavMesh
но столкнулся с некоторыми неприятными моментами:
Актер не имеет коллизий и соответственно доступны для навигации сквозь них.
При добавлении коллизий (
UBoxComponent
)NavMesh
обновляется только после небольшого перемещения актеров в редакторе. На форумах писали о баге с навигацией в 4 версии, но похоже этот баг есть и в 5 версии.
Поэтому я решил использовать FNavLocalGridData
. Это, по сути, сетка с изменяемыми размерами и положением.
В FNavLocalGridData
имеется метод FindPath(const FIntVector& StartCoords, const FIntVector& EndCoords, TArray<FIntVector>& PathCoords)
, который использует алгоритм A-star для нахождения оптимального пути с учетом препятствий. Полученный путь затем используется для перемещения камеры.
Препятствие на FNavLocalGridData
отмечается с помощью метода MarkPointObstacle(const FVector& Center)
этот метод помечает одну ячейку как препятствие. Так же можно отмечать области из нескольких ячеек сразу методы MarkBoxObstacle
, MarkCapsuleObstacle
подробнее можно посмотреть в документации.
Поскольку размер мира может изменяться и быть очень большим я задал сетке фиксированный размер и при достижении камерой конечной точки, позиция сетки обновляется до текущей позиции камеры. Это позволило избежать проблем с производительностью и обеспечило плавное перемещение камеры. Если сетка выходит за пределы мира, её ячейки, находящиеся за пределами, помечаются как препятствия, гарантируя, что камера останется в пределах уровня и не уйдет за его границы.
Для обозначения препятствий в местах пересечения границ актеров используется следующий алгоритм:
Сначала определяются ближайшие актеры, то есть те актеры, границы которых пересекаются с границами сетки.
Затем для каждого ближайшего актера проверяются его границы на предмет вхождения ячеек сетки и, если ячейка входит в эти границы, она отмечается как препятствие.
Этот подход позволяет избежать необходимости добавлять коллизии, чтобы предотвратить прохождение камеры сквозь актеров.
Так как USD Importer не поддерживает импорт коллизий и в случае с NavMesh
пришлось добавлять коллизии и столкнуться с багом упомянутым выше. Отказ же от NavMesh
в пользу FNavLocalGridData
позволил избежать этих проблем и обеспечил более надежную и гибкую навигацию для камеры.