Unreal : NavMesh Edge Sliding

1 분 소요

왜 하는 것이냐?

기존에 네비 메쉬가 아닌 곳으로 가면 그대로 멈춤

이 부분이 아쉬워서 슬라이딩 되게 추가 개발 요청

원래 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를 사용.

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;
}

태그:

카테고리:

업데이트: