Кастомная навигация в Unreal Engine 5 и USD импорт файлов

2a261384ca7970b81e4a239804083a85

Перед мной возникла задача создать демку на 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, но столкнулся с некоторыми неприятными моментами:

  1. Актер не имеет коллизий и соответственно доступны для навигации сквозь них.

  2. При добавлении коллизий (UBoxComponent) NavMesh обновляется только после небольшого перемещения актеров в редакторе. На форумах писали о баге с навигацией в 4 версии, но похоже этот баг есть и в 5 версии.

Поэтому я решил использовать FNavLocalGridData. Это, по сути, сетка с изменяемыми размерами и положением.

В FNavLocalGridData имеется метод FindPath(const FIntVector& StartCoords, const FIntVector& EndCoords, TArray& PathCoords), который использует алгоритм A-star для нахождения оптимального пути с учетом препятствий. Полученный путь затем используется для перемещения камеры.

Препятствие на FNavLocalGridData отмечается с помощью метода MarkPointObstacle(const FVector& Center) этот метод помечает одну ячейку как препятствие. Так же можно отмечать области из нескольких ячеек сразу методы MarkBoxObstacle, MarkCapsuleObstacle подробнее можно посмотреть в документации.

Поскольку размер мира может изменяться и быть очень большим я задал сетке фиксированный размер и при достижении камерой конечной точки, позиция сетки обновляется до текущей позиции камеры. Это позволило избежать проблем с производительностью и обеспечило плавное перемещение камеры. Если сетка выходит за пределы мира, её ячейки, находящиеся за пределами, помечаются как препятствия, гарантируя, что камера останется в пределах уровня и не уйдет за его границы.

Для обозначения препятствий в местах пересечения границ актеров используется следующий алгоритм:

  1. Сначала определяются ближайшие актеры, то есть те актеры, границы которых пересекаются с границами сетки.

  2. Затем для каждого ближайшего актера проверяются его границы на предмет вхождения ячеек сетки и, если ячейка входит в эти границы, она отмечается как препятствие.

Этот подход позволяет избежать необходимости добавлять коллизии, чтобы предотвратить прохождение камеры сквозь актеров.

Так как USD Importer не поддерживает импорт коллизий и в случае с NavMesh пришлось добавлять коллизии и столкнуться с багом упомянутым выше. Отказ же от NavMesh в пользу FNavLocalGridData позволил избежать этих проблем и обеспечил более надежную и гибкую навигацию для камеры.

© Habrahabr.ru