void ANavigationTestingActor::SearchPathTo(ANavigationTestingActor* Goal) { #if WITH_EDITORONLY_DATA if (EdRenderComp) { EdRenderComp->MarkRenderStateDirty(); } #endif // WITH_EDITORONLY_DATA if (Goal == NULL) { return; } UNavigationSystem* NavSys = GetWorld()->GetNavigationSystem(); check(NavSys); const double StartTime = FPlatformTime::Seconds(); FPathFindingQuery Query = BuildPathFindingQuery(Goal); EPathFindingMode::Type Mode = bUseHierarchicalPathfinding ? EPathFindingMode::Hierarchical : EPathFindingMode::Regular; FPathFindingResult Result = NavSys->FindPathSync(NavAgentProps, Query, Mode); const double EndTime = FPlatformTime::Seconds(); const float Duration = (EndTime - StartTime); PathfindingTime = Duration * 1000000.0f; // in micro seconds [us] bPathIsPartial = Result.IsPartial(); bPathExist = Result.IsSuccessful(); bPathSearchOutOfNodes = bPathExist ? Result.Path->DidSearchReachedLimit() : false; LastPath = Result.Path; PathCost = bPathExist ? Result.Path->GetCost() : 0.0f; if (bPathExist) { LastPath->AddObserver(PathObserver); if (OffsetFromCornersDistance > 0.0f) { ((FNavMeshPath*)LastPath.Get())->OffsetFromCorners(OffsetFromCornersDistance); } } #if WITH_RECAST && WITH_EDITORONLY_DATA if (bGatherDetailedInfo && !bUseHierarchicalPathfinding) { ARecastNavMesh* RecastNavMesh = Cast<ARecastNavMesh>(MyNavData); if (RecastNavMesh && RecastNavMesh->HasValidNavmesh()) { PathfindingSteps = RecastNavMesh->DebugPathfinding(Query, DebugSteps); } } #endif }
void UNavigationComponent::OnSmartLinkBroadcast(class USmartNavLinkComponent* NearbyLink) { // update only when agent is actually moving if (NearbyLink == NULL || !Path.IsValid() || PathFollowComp == NULL || PathFollowComp->GetStatus() == EPathFollowingStatus::Idle) { return; } UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld()); if (NavSys == NULL || MyNavAgent == NULL || GetNavData() == NULL) { return; } const bool bHasLink = Path->ContainsSmartLink(NearbyLink); const bool bIsEnabled = NearbyLink->IsEnabled(); if (bIsEnabled == bHasLink) { // enabled link, path already use it - or - disabled link and path it's not using it return; } const FVector GoalLocation = Path->PathPoints.Last().Location; FPathFindingResult Result = NavSys->FindPathSync(*MyNavAgent->GetNavAgentProperties(), FPathFindingQuery(GetNavData(), MyNavAgent->GetNavAgentLocation(), GoalLocation, StoredQueryFilter), bDoHierarchicalPathfinding ? EPathFindingMode::Hierarchical : EPathFindingMode::Regular); if (Result.IsSuccessful() || Result.IsPartial()) { const bool bNewHasLink = Result.Path->ContainsSmartLink(NearbyLink); if ((bIsEnabled && !bHasLink && bNewHasLink) || (!bIsEnabled && bHasLink && !bNewHasLink)) { SetPath(Result.Path); NotifyPathUpdate(); } } }
bool UNavigationComponent::GeneratePath(const FVector& FromLocation, const FVector& GoalLocation, TSharedPtr<const FNavigationQueryFilter> QueryFilter) { if (GetWorld() == NULL || GetWorld()->GetNavigationSystem() == NULL || GetOuter() == NULL || MyNavAgent == NULL || GetNavData() == NULL) { return false; } const FNavLocation NavMeshGoalLocation = ProjectPointToNavigation(GoalLocation); const EPathFindingMode::Type Mode = bDoHierarchicalPathfinding ? EPathFindingMode::Hierarchical : EPathFindingMode::Regular; FPathFindingResult Result = GetWorld()->GetNavigationSystem()->FindPathSync(*MyNavAgent->GetNavAgentProperties() , FPathFindingQuery(GetNavData(), FromLocation, NavMeshGoalLocation.Location, QueryFilter) , Mode); if (Result.IsSuccessful() || Result.IsPartial()) { CurrentGoal = NavMeshGoalLocation.Location; SetPath(Result.Path); return true; } return false; }
bool UNavigationComponent::GeneratePathTo(const FVector& GoalLocation, TSharedPtr<const FNavigationQueryFilter> QueryFilter) { if (GetWorld() == NULL || GetWorld()->GetNavigationSystem() == NULL || GetOuter() == NULL) { return false; } // Make sure we're trying to path to the closest valid location TO that location rather than the absolute location. // After talking with Steve P., if we need to ever allow pathing WITHOUT projecting to nav-mesh, it will probably be // best to do so using a flag while keeping this as the default behavior. NOTE:` If any changes to this behavior // are made, you should search for other places in UNavigationComponent using NavMeshGoalLocation and make sure the // behavior remains consistent. // make sure that nav data is updated GetNavData(); const FNavAgentProperties* NavAgentProps = MyNavAgent ? MyNavAgent->GetNavAgentProperties() : NULL; if (NavAgentProps != NULL) { FVector NavMeshGoalLocation; #if ENABLE_VISUAL_LOG UE_VLOG_LOCATION(GetOwner(), GoalLocation, 34, FColor(0,127,14), TEXT("GoalLocation")); const FVector ProjectionExtent = MyNavData ? MyNavData->GetDefaultQueryExtent() : FVector(DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_HORIZONTAL, DEFAULT_NAV_QUERY_EXTENT_VERTICAL); UE_VLOG_BOX(GetOwner(), FBox(GoalLocation - ProjectionExtent, GoalLocation + ProjectionExtent), FColor::Green, TEXT_EMPTY); #endif if (ProjectPointToNavigation(GoalLocation, NavMeshGoalLocation) == QuerySuccess) { UE_VLOG_SEGMENT(GetOwner(), GoalLocation, NavMeshGoalLocation, FColor::Blue, TEXT_EMPTY); UE_VLOG_LOCATION(GetOwner(), NavMeshGoalLocation, 34, FColor::Blue, TEXT_EMPTY); UNavigationSystem* NavSys = GetWorld()->GetNavigationSystem(); FPathFindingQuery Query(MyNavData, MyNavAgent->GetNavAgentLocation(), NavMeshGoalLocation, QueryFilter); const EPathFindingMode::Type Mode = bDoHierarchicalPathfinding ? EPathFindingMode::Hierarchical : EPathFindingMode::Regular; FPathFindingResult Result = NavSys->FindPathSync(*MyNavAgent->GetNavAgentProperties(), Query, Mode); // try reversing direction if (bSearchFromGoalWhenOutOfNodes && !bDoHierarchicalPathfinding && Result.Path.IsValid() && Result.Path->DidSearchReachedLimit()) { // quick check if path exists bool bPathExists = false; { DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Pathfinding: HPA* test time"), STAT_Navigation_PathVerifyTime, STATGROUP_Navigation); bPathExists = NavSys->TestPathSync(Query, EPathFindingMode::Hierarchical); } UE_VLOG(GetOwner(), LogNavigation, Log, TEXT("GeneratePathTo: out of nodes, HPA* path: %s"), bPathExists ? TEXT("exists, trying reversed search") : TEXT("doesn't exist")); // reverse search if (bPathExists) { DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Pathfinding: reversed test time"), STAT_Navigation_PathReverseTime, STATGROUP_Navigation); TSharedPtr<FNavigationQueryFilter> Filter = QueryFilter.IsValid() ? QueryFilter->GetCopy() : MyNavData->GetDefaultQueryFilter()->GetCopy(); Filter->SetBacktrackingEnabled(true); FPathFindingQuery ReversedQuery(MyNavData, Query.EndLocation, Query.StartLocation, Filter); Result = NavSys->FindPathSync(*MyNavAgent->GetNavAgentProperties(), Query, Mode); } } if (Result.IsSuccessful() || Result.IsPartial()) { CurrentGoal = NavMeshGoalLocation; SetPath(Result.Path); if (IsFollowing() == true) { // make sure ticking is enabled (and it shouldn't be enabled before _first_ async path finding) SetComponentTickEnabledAsync(true); } NotifyPathUpdate(); return true; } else { UE_VLOG(GetOwner(), LogNavigation, Display, TEXT("Failed to generate path to destination")); } } else { UE_VLOG(GetOwner(), LogNavigation, Display, TEXT("Destination not on navmesh (and nowhere near!)")); } } else { UE_VLOG(GetOwner(), LogNavigation, Display, TEXT("UNavigationComponent::GeneratePathTo: NavAgentProps == NULL (Probably Pawn died)")); } return false; }
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); } }