void UNavigationPath::SetPath(FNavPathSharedPtr NewSharedPath) { FNavigationPath* NewPath = NewSharedPath.Get(); if (SharedPath.Get() != NewPath) { if (SharedPath.IsValid()) { SharedPath->RemoveObserver(PathObserverDelegateHandle); } SharedPath = NewSharedPath; if (NewPath != NULL) { PathObserverDelegateHandle = NewPath->AddObserver(PathObserver); if (RecalculateOnInvalidation != ENavigationOptionFlag::Default) { NewPath->EnableRecalculationOnInvalidation(RecalculateOnInvalidation == ENavigationOptionFlag::Enable); } SetPathPointsFromPath(*NewPath); } else { PathPoints.Reset(); } OnPathEvent(NewPath, NewPath != NULL ? ENavPathEvent::NewPath : ENavPathEvent::Cleared); } }
void UNavigationPath::SetPathPointsFromPath(FNavigationPath& NativePath) { PathPoints.Reset(NativePath.GetPathPoints().Num()); for (const auto& PathPoint : NativePath.GetPathPoints()) { PathPoints.Add(PathPoint.Location); } }
void FGameplayDebuggerCategory_AI::CollectPathData(AAIController* DebugAI) { UPathFollowingComponent* PathComp = DebugAI ? DebugAI->GetPathFollowingComponent() : nullptr; DataPack.bIsUsingPathFollowing = (PathComp != nullptr); if (PathComp) { TArray<FString> Tokens; TArray<EPathFollowingDebugTokens::Type> Flags; PathComp->GetDebugStringTokens(Tokens, Flags); for (int32 Idx = 0; Idx < Tokens.Num(); Idx++) { switch (Flags[Idx]) { case EPathFollowingDebugTokens::Description: DataPack.PathFollowingInfo += Tokens[Idx]; break; case EPathFollowingDebugTokens::ParamName: DataPack.PathFollowingInfo += TEXT(", {yellow}"); DataPack.PathFollowingInfo += Tokens[Idx]; DataPack.PathFollowingInfo += TEXT(":"); break; case EPathFollowingDebugTokens::PassedValue: DataPack.PathFollowingInfo += TEXT("{yellow}"); DataPack.PathFollowingInfo += Tokens[Idx]; break; case EPathFollowingDebugTokens::FailedValue: DataPack.PathFollowingInfo += TEXT("{orange}"); DataPack.PathFollowingInfo += Tokens[Idx]; break; default: break; } } FNavigationPath* CurrentPath = PathComp->GetPath().Get(); if (CurrentPath) { DataPack.bPathHasGoalActor = (CurrentPath->GetGoalActor() != nullptr); DataPack.PathGoalLocation = CurrentPath->GetGoalLocation(); DataPack.NextPathPointIndex = PathComp->GetNextPathIndex(); } if ((CurrentPath != RawLastPath) || (CurrentPath && (CurrentPath->GetLastUpdateTime() != LastPathUpdateTime) )) { RawLastPath = CurrentPath; PathDataPack = FRepDataPath(); if (CurrentPath) { LastPathUpdateTime = CurrentPath->GetLastUpdateTime(); const FNavMeshPath* NavMeshPath = CurrentPath->CastPath<FNavMeshPath>(); const ARecastNavMesh* NavData = Cast<const ARecastNavMesh>(CurrentPath->GetNavigationDataUsed()); if (NavMeshPath && NavData) { for (int32 Idx = 0; Idx < NavMeshPath->PathCorridor.Num(); Idx++) { FRepDataPath::FPoly PolyData; NavData->GetPolyVerts(NavMeshPath->PathCorridor[Idx], PolyData.Points); const uint32 AreaId = NavData->GetPolyAreaID(NavMeshPath->PathCorridor[Idx]); PolyData.Color = NavData->GetAreaIDColor(AreaId); PathDataPack.PathCorridor.Add(PolyData); } } for (int32 Idx = 0; Idx < CurrentPath->GetPathPoints().Num(); Idx++) { PathDataPack.PathPoints.Add(CurrentPath->GetPathPoints()[Idx].Location); } } } } }
void ANavigationData::TickActor(float DeltaTime, enum ELevelTick TickType, FActorTickFunction& ThisTickFunction) { Super::TickActor(DeltaTime, TickType, ThisTickFunction); PurgeUnusedPaths(); INC_DWORD_STAT_BY(STAT_Navigation_ObservedPathsCount, ObservedPaths.Num()); if (NextObservedPathsTickInSeconds >= 0.f) { NextObservedPathsTickInSeconds -= DeltaTime; if (NextObservedPathsTickInSeconds <= 0.f) { RepathRequests.Reserve(ObservedPaths.Num()); for (int32 PathIndex = ObservedPaths.Num() - 1; PathIndex >= 0; --PathIndex) { if (ObservedPaths[PathIndex].IsValid()) { FNavPathSharedPtr SharedPath = ObservedPaths[PathIndex].Pin(); FNavigationPath* Path = SharedPath.Get(); EPathObservationResult::Type Result = Path->TickPathObservation(); switch (Result) { case EPathObservationResult::NoLongerObserving: ObservedPaths.RemoveAtSwap(PathIndex, 1, /*bAllowShrinking=*/false); break; case EPathObservationResult::NoChange: // do nothing break; case EPathObservationResult::RequestRepath: RepathRequests.Add(FNavPathRecalculationRequest(SharedPath, ENavPathUpdateType::GoalMoved)); break; default: check(false && "unhandled EPathObservationResult::Type in ANavigationData::TickActor"); break; } } else { ObservedPaths.RemoveAtSwap(PathIndex, 1, /*bAllowShrinking=*/false); } } if (ObservedPaths.Num() > 0) { NextObservedPathsTickInSeconds = ObservedPathsTickInterval; } } } if (RepathRequests.Num() > 0) { float TimeStamp = GetWorldTimeStamp(); TArray<FNavPathRecalculationRequest> PostponedRequests; const UWorld* World = GetWorld(); // @todo batch-process it! for (auto RecalcRequest : RepathRequests) { // check if it can be updated right now FNavPathSharedPtr PinnedPath = RecalcRequest.Path.Pin(); if (PinnedPath.IsValid() == false) { continue; } const UObject* PathQuerier = PinnedPath->GetQuerier(); const INavAgentInterface* PathNavAgent = Cast<const INavAgentInterface>(PathQuerier); if (PathNavAgent && PathNavAgent->ShouldPostponePathUpdates()) { PostponedRequests.Add(RecalcRequest); continue; } FPathFindingQuery Query(PinnedPath.ToSharedRef()); // @todo consider supplying NavAgentPropertied from path's querier const FPathFindingResult Result = FindPath(FNavAgentProperties(), Query.SetPathInstanceToUpdate(PinnedPath)); // update time stamp to give observers any means of telling if it has changed PinnedPath->SetTimeStamp(TimeStamp); // partial paths are still valid and can change to full path when moving goal gets back on navmesh if (Result.IsSuccessful() || Result.IsPartial()) { PinnedPath->UpdateLastRepathGoalLocation(); PinnedPath->DoneUpdating(RecalcRequest.Reason); if (RecalcRequest.Reason == ENavPathUpdateType::NavigationChanged) { RegisterActivePath(PinnedPath); } } else { PinnedPath->RePathFailed(); } } RepathRequests.Reset(); RepathRequests.Append(PostponedRequests); } }