Unreal : NavMesh Edge Sliding
NavMesh Edge Sliding
왜 하는 것이냐?
기존에 네비 메쉬가 아닌 곳으로 가면 그대로 멈춤
이 부분이 아쉬워서 슬라이딩 되게 추가 개발 요청
원래 CharacterMovementComponent의.. 슬라이딩 처리는? 벽을 향하는 Delta로 SafeMoveUpdatedComponent 호출 시 나온 HitResult의 Normal과 내 캐릭터 Delta값의 VectorPlaneProject로 나온 SlideDelta값으로 처리한다.
또한, 이동을 모두 네비 메쉬 위에서만 이동 되게 하여 HitResult를 가져올 수 없다!
그럼 네비 메쉬의 슬라이딩 처리는 어떻게 해야 할까?
충돌 박스 근처론 갈 수 없음으로, 네비 메쉬 엣지와 부딪혔을 때 나오는 Normal만 알면 위 공식(VectorPlaneProject) 그대로 사용하면 된다.
어떻게 네비 메쉬 엣지의 Normal을 알 수 있을까?
결론은 Detour를 뒤져야 한다…
엣지 정보를 얻을 수 함수를 찾음.
dtStatus findDistanceToWall(dtPolyRef startRef, const float* centerPos, const float maxRadius, const dtQueryFilter* filter, float* hitDist, float* hitPos, float* hitNormal) const;
dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos,const dtQueryFilter* filter, float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const;
findDistanceWall은 내 방향이 고려 안되므로, raycast를 사용.
함수를 보면 비용이 싼 편이다. maxPath만 1로 줘도 현재 polyRef만 검색한다. 또한, 현 PolyRef의 link 수 만큼만 while문을 돌기 때문에 비싸지 않다.
bool FindNavSlidingDelta(const FVector& InDeltaMove, const FVector& InOldLocation, FVector& OutAdjustedLocation, FNavLocation& OutNavLocation) const
{
FNavLocation testLocation = CachedNavLocation;
const ARecastNavMesh* recast = Cast<ARecastNavMesh>(GetNavData());
const FPImplRecastNavMesh* navMeshImpl = recast->GetRecastNavMeshImpl();
const auto& navFilter = *(UNavigationQueryFilter::GetQueryFilter(*recast, CharacterOwner, UB2DefaultNavigationQueryFilter::StaticClass()));
const FRecastQueryFilter* filterImplementation = (const FRecastQueryFilter*)(navFilter.GetImplementation());
const dtQueryFilter* queryFilter = filterImplementation->GetAsDetourQueryFilter();
dtNavMeshQuery& navQueryVariable = navMeshImpl->SharedNavQuery;
FRecastSpeciaLinkFilter linkFilter(FNavigationSystem::GetCurrent<UNavigationSystemV1>(recast->GetWorld()), CharacterOwner);
navQueryVariable.init(navMeshImpl->DetourNavMesh, navFilter.GetMaxSearchNodes(), &linkFilter);
const FVector recastStartPt = Unreal2RecastPoint(testLocation.Location);
const FVector recastEndPt = Unreal2RecastPoint(OutAdjustedLocation);
float time = 0.f;
float hitNormal[3] = { 0.f, };
int pathCount = 0;
dtPolyRef findWallRef;
const int maxPathCount = 2;
const dtStatus raycastQuery = navQueryVariable.raycast(
testLocation.NodeRef, &recastStartPt.X, &recastEndPt.X, queryFilter, &time, hitNormal, &findWallRef, &pathCount, maxPathCount);
const FVector unrealHitNormal = Recast2UnrealPoint(hitNormal);
if (dtStatusFailed(raycastQuery))
{
return false;
}
FVector slideDelta = FVector::VectorPlaneProject(InDeltaMove, unrealHitNormal);
slideDelta.Z = 0.f;
const FVector newLocation = InOldLocation + slideDelta;
FNavLocation temp;
if (!FindNavFloor(newLocation, temp))
{
OutAdjustedLocation = InOldLocation;
OutNavLocation = testLocation;
return true;
}
OutAdjustedLocation = newLocation;
OutNavLocation = temp;
return true;
}