// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_4 #include "CoreMinimal.h" #endif #include "Stats/Stats.h" #include "UObject/ObjectMacros.h" #include "Templates/SubclassOf.h" #include "Engine/EngineTypes.h" #include "Async/TaskGraphInterfaces.h" #include "GameFramework/Actor.h" #if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_4 #include "NavFilters/NavigationQueryFilter.h" #endif #include "AI/Navigation/NavigationTypes.h" #include "NavigationSystemTypes.h" #include "EngineDefines.h" #include "AI/Navigation/NavigationDataInterface.h" #include "NavigationData.generated.h" class ANavigationData; class Error; class FNavDataGenerator; class INavAgentInterface; class INavLinkCustomInterface; class UNavArea; class UPrimitiveComponent; class UNavigationQueryFilter; struct FNavigationDirtyArea; USTRUCT() struct FSupportedAreaData { GENERATED_USTRUCT_BODY() UPROPERTY() FString AreaClassName; UPROPERTY() int32 AreaID; UPROPERTY(transient) TObjectPtr AreaClass; NAVIGATIONSYSTEM_API FSupportedAreaData(TSubclassOf NavAreaClass = NULL, int32 InAreaID = INDEX_NONE); }; struct FNavPathRecalculationRequest { FNavPathWeakPtr Path; ENavPathUpdateType::Type Reason; FNavPathRecalculationRequest(const FNavPathSharedPtr& InPath, ENavPathUpdateType::Type InReason) : Path(InPath.ToSharedRef()), Reason(InReason) {} bool operator==(const FNavPathRecalculationRequest& Other) const { return Path == Other.Path; } }; struct FPathFindingResult { FNavPathSharedPtr Path; ENavigationQueryResult::Type Result; FPathFindingResult(ENavigationQueryResult::Type InResult = ENavigationQueryResult::Invalid) : Result(InResult) { } FORCEINLINE bool IsSuccessful() const { return Result == ENavigationQueryResult::Success; } FORCEINLINE bool IsPartial() const; }; struct FNavigationRaycastAdditionalResults { /** When the ray is not obstructed, indicates if the projection of RayEnd is located at the end of the explored corridor. * When bIsRaytEndInCorridor is false, it means that RayEnd failed to project to the NavigationData or on a navigation node that is not part of the explored corridor (e.g. different height) */ bool bIsRayEndInCorridor = false; }; struct FNavigationPath : public TSharedFromThis { //DECLARE_DELEGATE_OneParam(FPathObserverDelegate, FNavigationPath*); DECLARE_MULTICAST_DELEGATE_TwoParams(FPathObserverDelegate, FNavigationPath*, ENavPathEvent::Type); NAVIGATIONSYSTEM_API FNavigationPath(); NAVIGATIONSYSTEM_API FNavigationPath(const TArray& Points, AActor* Base = NULL); NAVIGATIONSYSTEM_API virtual ~FNavigationPath(); PRAGMA_DISABLE_DEPRECATION_WARNINGS FNavigationPath(const FNavigationPath&) = default; FNavigationPath& operator=(const FNavigationPath& Other) = default; PRAGMA_ENABLE_DEPRECATION_WARNINGS FORCEINLINE bool IsValid() const { return bIsReady && PathPoints.Num() > 1 && bUpToDate; } FORCEINLINE bool IsUpToDate() const { return bUpToDate; } FORCEINLINE bool IsReady() const { return bIsReady; } FORCEINLINE bool IsPartial() const { return bIsPartial; } FORCEINLINE bool DidSearchReachedLimit() const { return bReachedSearchLimit; } // True when the path request failed because the start location couldn't be projected on the NavData. // False doesn't guarantee that the location is navigable, it's also possible a previous error skipped the projection altogether bool IsErrorStartLocationNonNavigable() const { return bErrorStartLocationNonNavigable; } // True when the path request failed because the end location couldn't be projected on the NavData. // False doesn't guarantee that the location is navigable, it's also possible a previous error skipped the projection altogether bool IsErrorEndLocationNonNavigable() const { return bErrorEndLocationNonNavigable; } FORCEINLINE bool IsWaitingForRepath() const { return bWaitingForRepath; } FORCEINLINE void SetManualRepathWaiting(const bool bInWaitingForRepath) { bWaitingForRepath = bInWaitingForRepath; } FORCEINLINE bool ShouldUpdateStartPointOnRepath() const { return bUpdateStartPointOnRepath; } FORCEINLINE bool ShouldUpdateEndPointOnRepath() const { return bUpdateEndPointOnRepath; } FORCEINLINE FVector GetDestinationLocation() const { return IsValid() ? PathPoints.Last().Location : INVALID_NAVEXTENT; } FORCEINLINE FPathObserverDelegate& GetObserver() { return ObserverDelegate; } FORCEINLINE FDelegateHandle AddObserver(FPathObserverDelegate::FDelegate NewObserver) { return ObserverDelegate.Add(NewObserver); } FORCEINLINE void RemoveObserver(FDelegateHandle HandleOfObserverToRemove) { ObserverDelegate.Remove(HandleOfObserverToRemove); } FORCEINLINE void MarkReady() { bIsReady = true; } NAVIGATIONSYSTEM_API FORCEINLINE void SetNavigationDataUsed(const ANavigationData* const NewData); FORCEINLINE ANavigationData* GetNavigationDataUsed() const { return NavigationDataUsed.Get(); } FORCEINLINE void SetQuerier(const UObject* InQuerier) { PathFindingQueryData.Owner = InQuerier; } FORCEINLINE const UObject* GetQuerier() const { return PathFindingQueryData.Owner.Get(); } FORCEINLINE void SetQueryData(const FPathFindingQueryData& QueryData) { PathFindingQueryData = QueryData; } FORCEINLINE FPathFindingQueryData GetQueryData() const { // return copy of query data return PathFindingQueryData; } //FORCEINLINE void SetObserver(const FPathObserverDelegate& Observer) { ObserverDelegate = Observer; } FORCEINLINE void SetIsPartial(const bool bPartial) { bIsPartial = bPartial; } FORCEINLINE void SetSearchReachedLimit(const bool bLimited) { bReachedSearchLimit = bLimited; } void SetErrorStartLocationNonNavigable(const bool bErrorNonNavigable) { bErrorStartLocationNonNavigable = bErrorNonNavigable; } void SetErrorEndLocationNonNavigable(const bool bErrorNonNavigable) { bErrorEndLocationNonNavigable = bErrorNonNavigable; } FORCEINLINE void SetFilter(FSharedConstNavQueryFilter InFilter) { PathFindingQueryData.QueryFilter = InFilter; Filter = InFilter; } FORCEINLINE FSharedConstNavQueryFilter GetFilter() const { return PathFindingQueryData.QueryFilter; } FORCEINLINE AActor* GetBaseActor() const { return Base.Get(); } FVector GetStartLocation() const { return PathPoints.Num() > 0 ? PathPoints[0].Location : FNavigationSystem::InvalidLocation; } FVector GetEndLocation() const { return PathPoints.Num() > 0 ? PathPoints.Last().Location : FNavigationSystem::InvalidLocation; } FORCEINLINE void DoneUpdating(ENavPathUpdateType::Type UpdateType) { static const ENavPathEvent::Type PathUpdateTypeToPathEvent[] = { ENavPathEvent::UpdatedDueToGoalMoved // GoalMoved, , ENavPathEvent::UpdatedDueToNavigationChanged // NavigationChanged, , ENavPathEvent::MetaPathUpdate // MetaPathUpdate, , ENavPathEvent::Custom // Custom, }; bUpToDate = true; bWaitingForRepath = false; if (bUseOnPathUpdatedNotify) { // notify path before observers OnPathUpdated(UpdateType); } ObserverDelegate.Broadcast(this, PathUpdateTypeToPathEvent[uint8(UpdateType)]); } FORCEINLINE double GetTimeStamp() const { return LastUpdateTimeStamp; } FORCEINLINE void SetTimeStamp(double TimeStamp) { LastUpdateTimeStamp = TimeStamp; } NAVIGATIONSYSTEM_API void Invalidate(); NAVIGATIONSYSTEM_API void RePathFailed(); /** Resets all variables describing generated path before attempting new pathfinding call. * This function will NOT reset setup variables like goal actor, filter, observer, etc */ NAVIGATIONSYSTEM_API virtual void ResetForRepath(); NAVIGATIONSYSTEM_API virtual void DebugDraw(const ANavigationData* NavData, const FColor PathColor, class UCanvas* Canvas, const bool bPersistent, const float LifeTime, const uint32 NextPathPointIndex = 0) const; #if ENABLE_VISUAL_LOG NAVIGATIONSYSTEM_API virtual void DescribeSelfToVisLog(struct FVisualLogEntry* Snapshot) const; NAVIGATIONSYSTEM_API virtual FString GetDescription() const; #endif // ENABLE_VISUAL_LOG /** check if path contains specific custom nav link */ UE_DEPRECATED(5.3, "Use version that takes FNavLinkId instead. This function only returns false.") virtual bool ContainsCustomLink(uint32 UniqueLinkId) const final { return false; } NAVIGATIONSYSTEM_API virtual bool ContainsCustomLink(FNavLinkId UniqueLinkId) const; /** check if path contains any custom nav link */ NAVIGATIONSYSTEM_API virtual bool ContainsAnyCustomLink() const; /** check if path contains given node */ NAVIGATIONSYSTEM_API virtual bool ContainsNode(NavNodeRef NodeRef) const; /** get cost of path, starting from given point */ virtual FVector::FReal GetCostFromIndex(int32 PathPointIndex) const { return 0.; } /** get cost of path, starting from given node */ virtual FVector::FReal GetCostFromNode(NavNodeRef PathNode) const { return 0.; } FORCEINLINE FVector::FReal GetCost() const { return GetCostFromIndex(0); } /** calculates total length of segments from NextPathPoint to the end of path, plus distance from CurrentPosition to NextPathPoint */ NAVIGATIONSYSTEM_API virtual FVector::FReal GetLengthFromPosition(FVector SegmentStart, uint32 NextPathPointIndex) const; FORCEINLINE FVector::FReal GetLength() const { return PathPoints.Num() ? GetLengthFromPosition(PathPoints[0].Location, 1) : 0.0f; } static bool GetPathPoint(const FNavigationPath* Path, uint32 PathVertIdx, FNavPathPoint& PathPoint) { if (Path && Path->GetPathPoints().IsValidIndex((int32)PathVertIdx)) { PathPoint = Path->PathPoints[PathVertIdx]; return true; } return false; } FORCEINLINE const TArray& GetPathPoints() const { return PathPoints; } FORCEINLINE TArray& GetPathPoints() { return PathPoints; } /** get based position of path point */ NAVIGATIONSYSTEM_API FBasedPosition GetPathPointLocation(uint32 Index) const; /** checks if given path, starting from StartingIndex, intersects with given AABB box */ NAVIGATIONSYSTEM_API virtual bool DoesIntersectBox(const FBox& Box, uint32 StartingIndex = 0, int32* IntersectingSegmentIndex = NULL, FVector* AgentExtent = NULL) const; /** checks if given path, starting from StartingIndex, intersects with given AABB box. This version uses AgentLocation as beginning of the path * with segment between AgentLocation and path's StartingIndex-th node treated as first path segment to check */ NAVIGATIONSYSTEM_API virtual bool DoesIntersectBox(const FBox& Box, const FVector& AgentLocation, uint32 StartingIndex = 0, int32* IntersectingSegmentIndex = NULL, FVector* AgentExtent = NULL) const; /** retrieves normalized direction vector to given path segment * for '0'-th segment returns same as for 1st segment */ NAVIGATIONSYSTEM_API virtual FVector GetSegmentDirection(uint32 SegmentEndIndex) const; private: NAVIGATIONSYSTEM_API bool DoesPathIntersectBoxImplementation(const FBox& Box, const FVector& StartLocation, uint32 StartingIndex, int32* IntersectingSegmentIndex, FVector* AgentExtent) const; /** reset variables describing built path, leaves setup variables required for rebuilding */ NAVIGATIONSYSTEM_API void InternalResetNavigationPath(); public: /** type safe casts */ template FORCEINLINE const PathClass* CastPath() const { return PathType.IsA(PathClass::Type) ? static_cast(this) : NULL; } template FORCEINLINE PathClass* CastPath() { return PathType.IsA(PathClass::Type) ? static_cast(this) : NULL; } /** enables path observing specified AActor's location and update itself if actor changes location */ NAVIGATIONSYSTEM_API void SetGoalActorObservation(const AActor& ActorToObserve, float TetherDistance); /** Modifies distance to the GoalActor at which we'll update the path */ void SetGoalActorTetherDistance(const float NewTetherDistace) { GoalActorLocationTetherDistanceSq = FMath::Square(NewTetherDistace); } /** turns goal actor location's observation */ NAVIGATIONSYSTEM_API void DisableGoalActorObservation(); /** set's up the path to use SourceActor's location in case of recalculation */ NAVIGATIONSYSTEM_API void SetSourceActor(const AActor& InSourceActor); const AActor* GetSourceActor() const { return SourceActor.Get(); } const INavAgentInterface* GetSourceActorAsNavAgent() const { return SourceActorAsNavAgent; } FVector GetLastRepathGoalLocation() const { return GoalActorLastLocation; } NAVIGATIONSYSTEM_API void UpdateLastRepathGoalLocation(); double GetLastUpdateTime() const { return LastUpdateTimeStamp; } float GetGoalActorTetherDistance() const { return FMath::Sqrt(GoalActorLocationTetherDistanceSq); } /** if enabled path will request recalculation if it gets invalidated due to a change to underlying navigation */ void EnableRecalculationOnInvalidation(bool bShouldAutoUpdate) { bDoAutoUpdateOnInvalidation = bShouldAutoUpdate; } bool WillRecalculateOnInvalidation() const { return bDoAutoUpdateOnInvalidation; } /** if ignoring, path will stay bUpToDate after being invalidated due to a change to underlying navigation (observer and auto repath will NOT be triggered!) */ void SetIgnoreInvalidation(bool bShouldIgnore) { bIgnoreInvalidation = bShouldIgnore; } bool GetIgnoreInvalidation() const { return bIgnoreInvalidation; } NAVIGATIONSYSTEM_API EPathObservationResult::Type TickPathObservation(); /** If GoalActor is set it retrieved its navigation location, if not retrieved last path point location */ NAVIGATIONSYSTEM_API FVector GetGoalLocation() const; /** retrieved location to start path finding from (in case of path recalculation) */ NAVIGATIONSYSTEM_API FVector GetPathFindingStartLocation() const; const AActor* GetGoalActor() const { return bObservingGoalActor ? GoalActor.Get() : nullptr; } const INavAgentInterface* GetGoalActorAsNavAgent() const { return GoalActor.IsValid() ? GoalActorAsNavAgent : NULL; } // @todo this is navigation-type specific and should not be implemented here. /** additional node refs used during path following shortcuts */ TArray ShortcutNodeRefs; protected: /** optional notify called when path finishes update, before broadcasting to observes - requires bUseOnPathUpdatedNotify flag set */ virtual void OnPathUpdated(ENavPathUpdateType::Type UpdateType) {}; /** * IMPORTANT: path is assumed to be valid if it contains _MORE_ than _ONE_ point * point 0 is path's starting point - if it's the only point on the path then there's no path per se */ TArray PathPoints; /** base actor, if exist path points locations will be relative to it */ TWeakObjectPtr Base; private: /** if set path will observe GoalActor's location and update itself if goal moves more then * @note only actual navigation paths can use this feature, meaning the ones associated with * a NavigationData instance (meaning NavigationDataUsed != NULL) */ TWeakObjectPtr GoalActor; /** cached result of GoalActor casting to INavAgentInterface */ const INavAgentInterface* GoalActorAsNavAgent; /** if set will be queried for location in case of path's recalculation */ TWeakObjectPtr SourceActor; /** cached result of PathSource casting to INavAgentInterface */ const INavAgentInterface* SourceActorAsNavAgent; protected: // DEPRECATED: filter used to build this path FSharedConstNavQueryFilter Filter; /** type of path */ static NAVIGATIONSYSTEM_API const FNavPathType Type; FNavPathType PathType; /** A delegate that will be called when path becomes invalid */ FPathObserverDelegate ObserverDelegate; /** "true" until navigation data used to generate this path has been changed/invalidated */ uint32 bUpToDate : 1; /** when false it means path instance has been created, but not filled with data yet */ uint32 bIsReady : 1; /** "true" when path is only partially generated, when goal is unreachable and path represent best guess */ uint32 bIsPartial : 1; /** set to true when path finding algorithm reached a technical limit (like limit of A* nodes). * This generally means path cannot be trusted to lead to requested destination * although it might lead closer to destination. */ uint32 bReachedSearchLimit : 1; /** "true" when the start location could not be projected on the navigation data */ uint32 bErrorStartLocationNonNavigable : 1 = false; /** "true" when the end location could not be projected on the navigation data */ uint32 bErrorEndLocationNonNavigable : 1 = false; /** if true path will request re-pathing if it gets invalidated due to underlying navigation changed */ uint32 bDoAutoUpdateOnInvalidation : 1; /** if true path will keep bUpToDate value after getting invalidated due to underlying navigation changed * (observer and auto repath will NOT be triggered!) * it's NOT safe to use if path relies on navigation data references (e.g. poly corridor) */ uint32 bIgnoreInvalidation : 1; /** if true path will use GetPathFindingStartLocation() for updating QueryData before repath */ uint32 bUpdateStartPointOnRepath : 1; /** if true path will use GetGoalLocation() for updating QueryData before repath */ uint32 bUpdateEndPointOnRepath : 1; /** set when path is waiting for recalc from navigation data */ uint32 bWaitingForRepath : 1; /** if true path will call OnPathUpdated notify */ uint32 bUseOnPathUpdatedNotify : 1; /** indicates whether at any point GoalActor was a valid Actor. Used as * an optimization in FNavigationPath::TickPathObservation */ uint32 bObservingGoalActor : 1; /** navigation data used to generate this path */ TWeakObjectPtr NavigationDataUsed; /** essential part of query used to generate this path */ FPathFindingQueryData PathFindingQueryData; /** gets set during path creation and on subsequent path's updates */ double LastUpdateTimeStamp; private: /* if GoalActor is set this is the distance we'll try to keep GoalActor from end of path. If GoalActor * moves more then this from the end of the path we'll recalculate the path */ float GoalActorLocationTetherDistanceSq; /** last location of goal actor that was used for repaths to prevent spamming when path is partial */ FVector GoalActorLastLocation; }; /** * Supported options for runtime navigation data generation */ UENUM() enum class ERuntimeGenerationType : uint8 { // No runtime generation, fully static navigation data Static, // Supports only navigation modifiers updates DynamicModifiersOnly, // Fully dynamic, supports geometry changes along with navigation modifiers Dynamic, // Only for legacy loading don't use it! LegacyGeneration UMETA(Hidden) }; /** * Represents abstract Navigation Data (sub-classed as NavMesh, NavGraph, etc) * Used as a common interface for all navigation types handled by NavigationSystem */ UCLASS(config=Engine, defaultconfig, NotBlueprintable, abstract, MinimalAPI) class ANavigationData : public AActor, public INavigationDataInterface { GENERATED_UCLASS_BODY() UPROPERTY(transient, duplicatetransient) TObjectPtr RenderingComp; protected: UPROPERTY() FNavDataConfig NavDataConfig; /** if set to true then this navigation data will be drawing itself when requested as part of "show navigation" */ UPROPERTY(Transient, EditAnywhere, Category=Display) uint32 bEnableDrawing:1; //----------------------------------------------------------------------// // game-time config //----------------------------------------------------------------------// /** By default navigation will skip the first update after being successfully loaded * setting bForceRebuildOnLoad to false can override this behavior */ UPROPERTY(config, EditAnywhere, Category = Runtime) uint32 bForceRebuildOnLoad : 1; /** Should this instance auto-destroy when there's no navigation system on * world when it gets created/loaded */ UPROPERTY(config, EditAnywhere, Category = Runtime) uint32 bAutoDestroyWhenNoNavigation : 1; /** If set, navigation data can act as default one in navigation system's queries */ UPROPERTY(config, EditAnywhere, Category = Runtime, AdvancedDisplay) uint32 bCanBeMainNavData : 1; /** If set, navigation data will be spawned in persistent level during rebuild if actor doesn't exist */ UPROPERTY(config, VisibleAnywhere, Category = Runtime, AdvancedDisplay) uint32 bCanSpawnOnRebuild : 1; /** If true, the NavMesh can be dynamically rebuilt at runtime. */ UPROPERTY(config) uint32 bRebuildAtRuntime_DEPRECATED:1; /** Navigation data runtime generation options */ UPROPERTY(EditAnywhere, Category = Runtime, config) ERuntimeGenerationType RuntimeGeneration; /** all observed paths will be processed every ObservedPathsTickInterval seconds */ UPROPERTY(EditAnywhere, Category = Runtime, config) float ObservedPathsTickInterval; public: //----------------------------------------------------------------------// // Life cycle //----------------------------------------------------------------------// //~ Begin UObject/AActor Interface NAVIGATIONSYSTEM_API virtual void PostInitProperties() override; NAVIGATIONSYSTEM_API virtual void PostInitializeComponents() override; NAVIGATIONSYSTEM_API virtual void PostLoad() override; #if WITH_EDITOR NAVIGATIONSYSTEM_API virtual void RerunConstructionScripts() override; NAVIGATIONSYSTEM_API virtual void PostEditUndo() override; bool IsBuildingOnLoad() const { return bIsBuildingOnLoad; } void SetIsBuildingOnLoad(bool bValue) { bIsBuildingOnLoad = bValue; } #endif // WITH_EDITOR NAVIGATIONSYSTEM_API virtual void Destroyed() override; NAVIGATIONSYSTEM_API virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; //~ End UObject Interface NAVIGATIONSYSTEM_API virtual void CleanUp(); NAVIGATIONSYSTEM_API virtual void ApplyWorldOffset(const FVector& InOffset, bool bWorldShift) override; protected: NAVIGATIONSYSTEM_API void RequestRegistration(); private: /** Simply unregisters self from navigation system and calls CleanUp */ NAVIGATIONSYSTEM_API void UnregisterAndCleanUp(); public: NAVIGATIONSYSTEM_API virtual void CleanUpAndMarkPendingKill(); FORCEINLINE bool IsRegistered() const { return bRegistered; } NAVIGATIONSYSTEM_API virtual void OnRegistered(); NAVIGATIONSYSTEM_API void OnUnregistered(); FORCEINLINE uint16 GetNavDataUniqueID() const { return NavDataUniqueID; } NAVIGATIONSYSTEM_API virtual void TickActor(float DeltaTime, enum ELevelTick TickType, FActorTickFunction& ThisTickFunction) override; virtual bool NeedsRebuild() const { return false; } NAVIGATIONSYSTEM_API virtual bool SupportsRuntimeGeneration() const; NAVIGATIONSYSTEM_API virtual bool SupportsStreaming() const; NAVIGATIONSYSTEM_API virtual void OnNavigationBoundsChanged(); virtual void FillNavigationDataChunkActor(const FBox& QueryBounds, class ANavigationDataChunkActor& DataChunkActor, FBox& OutTilesBounds) const {}; virtual void OnStreamingNavDataAdded(class ANavigationDataChunkActor& InActor) {}; virtual void OnStreamingNavDataRemoved(class ANavigationDataChunkActor& InActor) {}; virtual void OnStreamingLevelAdded(ULevel* InLevel, UWorld* InWorld) {}; virtual void OnStreamingLevelRemoved(ULevel* InLevel, UWorld* InWorld) {}; #if WITH_EDITOR virtual double GetWorldPartitionNavigationDataBuilderOverlap() const { return 0; } #endif //----------------------------------------------------------------------// // Generation & data access //----------------------------------------------------------------------// public: FORCEINLINE const FNavDataConfig& GetConfig() const { return NavDataConfig; } FORCEINLINE ERuntimeGenerationType GetRuntimeGenerationMode() const { return RuntimeGeneration; } /** Populates NavDataConfig and sets NavAgentProperties with the Src config * Should be used when initially configuring the navdata and not when updating * values used in NavDataConfig */ NAVIGATIONSYSTEM_API virtual void SetConfig(const FNavDataConfig& Src); void SetSupportsDefaultAgent(bool bIsDefault) { bSupportsDefaultAgent = bIsDefault; SetNavRenderingEnabled(bIsDefault); } bool IsSupportingDefaultAgent() const { return bSupportsDefaultAgent; } const FNavAgentProperties& GetNavAgentProperties() const { return NavAgentProperties; } NAVIGATIONSYSTEM_API virtual bool DoesSupportAgent(const FNavAgentProperties& AgentProps) const; virtual void RestrictBuildingToActiveTiles(bool InRestrictBuildingToActiveTiles) {} bool CanBeMainNavData() const { return bCanBeMainNavData; } bool CanSpawnOnRebuild() const { return bCanSpawnOnRebuild; } bool NeedsRebuildOnLoad() const { return bForceRebuildOnLoad; } protected: virtual void FillConfig(FNavDataConfig& Dest) { Dest = NavDataConfig; } public: /** Creates new generator in case navigation supports it */ NAVIGATIONSYSTEM_API virtual void ConditionalConstructGenerator(); /** Any loading before NavDataGenerator->RebuildAll() */ virtual void LoadBeforeGeneratorRebuild() {} /** Runs after LoadBeforeGeneratorRebuild but before the rebuild. */ virtual void PostLoadPreRebuild() {} /** Triggers rebuild in case navigation supports it */ NAVIGATIONSYSTEM_API virtual void RebuildAll(); /** Blocks until navigation build is complete */ NAVIGATIONSYSTEM_API virtual void EnsureBuildCompletion(); /** Cancels current build */ NAVIGATIONSYSTEM_API virtual void CancelBuild(); /** Ticks navigation build * If the generator is set to time sliced rebuild then this function will only get called when * there is sufficient time (effectively roughly once in n frames where n is the number of time sliced nav data generators currently building) */ NAVIGATIONSYSTEM_API virtual void TickAsyncBuild(float DeltaSeconds); /** Retrieves navigation data generator */ FNavDataGenerator* GetGenerator() { return NavDataGenerator.Get(); } const FNavDataGenerator* GetGenerator() const { return NavDataGenerator.Get(); } /** Request navigation data update after changes in nav octree */ NAVIGATIONSYSTEM_API virtual void RebuildDirtyAreas(const TArray& DirtyAreas); /** Configures this NavData instance's navigation generation to be suspended * or active. It's active by default. If Suspended then all calls to * RebuildDirtyAreas will result in caching the request in SuspendedDirtyAreas * until SetRebuildingSuspended(false) gets call at which time all the contents * of SuspendedDirtyAreas will get pushed to the nav generator and SuspendedDirtyAreas * will be cleaned out. * Note that calling SetRebuildingSuspended(true) won't suspend the nav generation * already in progress. * Warning: Leaving generation suspended for extended periods of time can trigger a RebuildAll(). * To prevent the queue from growing out of control, it is limited by ai.navigation.MaxDirtyAreasCountWhileSuspended * If that limit is reached, upon resuming, the entire navigation data will be rebuilt. */ NAVIGATIONSYSTEM_API virtual void SetRebuildingSuspended(const bool bNewSuspend); /** Retrieves if this NavData instance's navigation generation is suspended */ virtual bool IsRebuildingSuspended() const { return bRebuildingSuspended; } /** Retrieves the number of suspended dirty areas */ virtual int32 GetNumSuspendedDirtyAreas() const { return SuspendedDirtyAreas.Num(); } /** releases navigation generator if any has been created */ protected: /** register self with navigation system as new NavAreaDefinition(s) observer */ NAVIGATIONSYSTEM_API void RegisterAsNavAreaClassObserver(); public: /** * Created an instance of navigation path of specified type. * PathType needs to derive from FNavigationPath */ template FNavPathSharedPtr CreatePathInstance(const FPathFindingQueryData& QueryData) const { FNavPathSharedPtr SharedPath = MakeShareable(new PathType()); SharedPath->SetNavigationDataUsed(this); SharedPath->SetQueryData(QueryData); SharedPath->SetTimeStamp( GetWorldTimeStamp() ); const_cast(this)->RegisterActivePath(SharedPath); return SharedPath; } void RegisterObservedPath(FNavPathSharedPtr SharedPath) { check(IsInGameThread()); if (ObservedPaths.Num() == 0) { NextObservedPathsTickInSeconds = ObservedPathsTickInterval; } ObservedPaths.Add(SharedPath); } void RequestRePath(FNavPathSharedPtr Path, ENavPathUpdateType::Type Reason) { check(IsInGameThread()); RepathRequests.AddUnique(FNavPathRecalculationRequest(Path, Reason)); } protected: /** removes from ActivePaths all paths that no longer have shared references (and are invalid in fact) */ NAVIGATIONSYSTEM_API void PurgeUnusedPaths(); NAVIGATIONSYSTEM_API void RegisterActivePath(FNavPathSharedPtr SharedPath); public: /** Returns bounding box for the navmesh. */ NAVIGATIONSYSTEM_API virtual FBox GetBounds() const PURE_VIRTUAL(ANavigationData::GetBounds,return FBox();); /** Returns list of navigable bounds. */ NAVIGATIONSYSTEM_API TArray GetNavigableBounds() const; /** Returns list of navigable bounds that belongs to specific level */ NAVIGATIONSYSTEM_API TArray GetNavigableBoundsInLevel(ULevel* InLevel) const; //----------------------------------------------------------------------// // Debug //----------------------------------------------------------------------// NAVIGATIONSYSTEM_API void DrawDebugPath(FNavigationPath* Path, const FColor PathColor = FColor::White, class UCanvas* Canvas = nullptr, const bool bPersistent = true, const float LifeTime = -1.f, const uint32 NextPathPointIndex = 0) const; FORCEINLINE bool IsDrawingEnabled() const { return bEnableDrawing; } /** @return Total mem counted, including super calls. */ NAVIGATIONSYSTEM_API virtual uint32 LogMemUsed() const; //----------------------------------------------------------------------// // Batch processing (important with async rebuilding) //----------------------------------------------------------------------// /** Starts batch processing and locks access to navigation data from other threads */ virtual void BeginBatchQuery() const {} /** Finishes batch processing and release locks */ virtual void FinishBatchQuery() const {}; //----------------------------------------------------------------------// // Querying //----------------------------------------------------------------------// FORCEINLINE FSharedConstNavQueryFilter GetDefaultQueryFilter() const { return DefaultQueryFilter; } FORCEINLINE const class INavigationQueryFilterInterface* GetDefaultQueryFilterImpl() const { return DefaultQueryFilter->GetImplementation(); } FORCEINLINE FVector GetDefaultQueryExtent() const { return NavDataConfig.DefaultQueryExtent; } /** * Synchronously looks for a path from @StartLocation to @EndLocation for agent with properties @AgentProperties. NavMesh actor appropriate for specified * FNavAgentProperties will be found automatically * @param ResultPath results are put here * @return true if path has been found, false otherwise * * @note don't make this function virtual! Look at implementation details and its comments for more info. */ FORCEINLINE FPathFindingResult FindPath(const FNavAgentProperties& AgentProperties, const FPathFindingQuery& Query) const { check(FindPathImplementation); // this awkward implementation avoids virtual call overhead - it's possible this function will be called a lot return (*FindPathImplementation)(AgentProperties, Query); } /** * Synchronously looks for a path from @StartLocation to @EndLocation for agent with properties @AgentProperties. NavMesh actor appropriate for specified * FNavAgentProperties will be found automatically * @param ResultPath results are put here * @return true if path has been found, false otherwise * * @note don't make this function virtual! Look at implementation details and its comments for more info. */ FORCEINLINE FPathFindingResult FindHierarchicalPath(const FNavAgentProperties& AgentProperties, const FPathFindingQuery& Query) const { check(FindHierarchicalPathImplementation); // this awkward implementation avoids virtual call overhead - it's possible this function will be called a lot return (*FindHierarchicalPathImplementation)(AgentProperties, Query); } /** * Synchronously checks if path between two points exists * FNavAgentProperties will be found automatically * @return true if path has been found, false otherwise * * @note don't make this function virtual! Look at implementation details and its comments for more info. */ FORCEINLINE bool TestPath(const FNavAgentProperties& AgentProperties, const FPathFindingQuery& Query, int32* NumVisitedNodes) const { check(TestPathImplementation); // this awkward implementation avoids virtual call overhead - it's possible this function will be called a lot return (*TestPathImplementation)(AgentProperties, Query, NumVisitedNodes); } /** * Synchronously checks if path between two points exists using hierarchical graph * FNavAgentProperties will be found automatically * @return true if path has been found, false otherwise * * @note don't make this function virtual! Look at implementation details and its comments for more info. */ FORCEINLINE bool TestHierarchicalPath(const FNavAgentProperties& AgentProperties, const FPathFindingQuery& Query, int32* NumVisitedNodes) const { check(TestHierarchicalPathImplementation); // this awkward implementation avoids virtual call overhead - it's possible this function will be called a lot return (*TestHierarchicalPathImplementation)(AgentProperties, Query, NumVisitedNodes); } /** * Synchronously makes a raycast on navigation data using QueryFilter * @param HitLocation if line was obstructed this will be set to hit location. Otherwise it contains SegmentEnd * @return true if line from RayStart to RayEnd is obstructed * * @note don't make this function virtual! Look at implementation details and its comments for more info. */ FORCEINLINE bool Raycast(const FVector& RayStart, const FVector& RayEnd, FVector& HitLocation, FSharedConstNavQueryFilter QueryFilter, const UObject* Querier = NULL) const { return Raycast(RayStart, RayEnd, HitLocation, nullptr/*AdditionalResults*/, QueryFilter, Querier); } /** * Synchronously makes a raycast on navigation data using QueryFilter * @param HitLocation if line was obstructed this will be set to hit location. Otherwise it contains SegmentEnd * @param AdditionalResults contains more information about the result of the raycast query. See FNavigationRaycastAdditionalResults description for details * @return true if line from RayStart to RayEnd is obstructed * * @note don't make this function virtual! Look at implementation details and its comments for more info. */ FORCEINLINE bool Raycast(const FVector& RayStart, const FVector& RayEnd, FVector& HitLocation, FNavigationRaycastAdditionalResults* AdditionalResults, FSharedConstNavQueryFilter QueryFilter, const UObject* Querier = NULL) const { check(RaycastImplementationWithAdditionalResults); // this awkward implementation avoids virtual call overhead - it's possible this function will be called a lot return (*RaycastImplementationWithAdditionalResults)(this, RayStart, RayEnd, HitLocation, AdditionalResults, QueryFilter, Querier); } /** Raycasts batched for efficiency */ NAVIGATIONSYSTEM_API virtual void BatchRaycast(TArray& Workload, FSharedConstNavQueryFilter QueryFilter, const UObject* Querier = NULL) const PURE_VIRTUAL(ANavigationData::BatchRaycast, ); /** Tries to move current nav location towards target constrained to navigable area. Faster than ProjectPointToNavmesh. * @param OutLocation if successful this variable will be filed with result * @return true if successful, false otherwise */ NAVIGATIONSYSTEM_API virtual bool FindMoveAlongSurface(const FNavLocation& StartLocation, const FVector& TargetPosition, FNavLocation& OutLocation, FSharedConstNavQueryFilter Filter = NULL, const UObject* Querier = NULL) const PURE_VIRTUAL(ANavigationData::FindMoveAlongSurface, return false;); /** Returns the navmesh edges that touch the convex polygon. The edges are not clipped by the polygon. * @param StartLocation a location on the navmesh where to start searching. * @param ConvexPolygon 2D convex polygon that describes the search area. * @param OutEdges result edges, each edge is an adjacent pair of points in the array. * @param Filter Nav filter to use, or if nullptr, default filter is used. * @return true if successful, false otherwise */ NAVIGATIONSYSTEM_API virtual bool FindOverlappingEdges(const FNavLocation& StartLocation, TConstArrayView ConvexPolygon, TArray& OutEdges, FSharedConstNavQueryFilter Filter = NULL, const UObject* Querier = NULL) const PURE_VIRTUAL(ANavigationData::FindOverlappingEdges, return false;); /** Searches navmesh edges between the two path points, search up to the convex polygon described in SearchArea. The returned edges are not clipped to the search area polygon. * @param Path Path where From and To belong to. * @param StartPoint start location of the path segment. * @param EndPoint end location of the path segment. * @param SearchArea 2D convex polygon that describes the search area. * @param OutEdges result edges, each edge is an adjacent pair of points in the array. * @param MaxAreaEnterCost if the fixed cost to enter a node is higher than this value, the node is considered unnavigable. * @param Filter Nav filter to use, or if nullptr, default filter is used. * @return true if successful, false otherwise */ NAVIGATIONSYSTEM_API virtual bool GetPathSegmentBoundaryEdges(const FNavigationPath& Path, const FNavPathPoint& StartPoint, const FNavPathPoint& EndPoint, const TConstArrayView SearchArea, TArray& OutEdges, const float MaxAreaEnterCost, FSharedConstNavQueryFilter Filter = NULL, const UObject* Querier = NULL) const PURE_VIRTUAL(ANavigationData::GetPathSegmentBoundaryEdges, return false;); NAVIGATIONSYSTEM_API virtual FNavLocation GetRandomPoint(FSharedConstNavQueryFilter Filter = NULL, const UObject* Querier = NULL) const PURE_VIRTUAL(ANavigationData::GetRandomPoint, return FNavLocation();); /** finds a random location in Radius, reachable from Origin */ NAVIGATIONSYSTEM_API virtual bool GetRandomReachablePointInRadius(const FVector& Origin, float Radius, FNavLocation& OutResult, FSharedConstNavQueryFilter Filter = NULL, const UObject* Querier = NULL) const PURE_VIRTUAL(ANavigationData::GetRandomReachablePointInRadius, return false;); /** finds a random location in navigable space, in given Radius */ NAVIGATIONSYSTEM_API virtual bool GetRandomPointInNavigableRadius(const FVector& Origin, float Radius, FNavLocation& OutResult, FSharedConstNavQueryFilter Filter = NULL, const UObject* Querier = NULL) const PURE_VIRTUAL(ANavigationData::GetRandomPointInNavigableRadius, return false;); /** Tries to project given Point to this navigation type, within given Extent. * @param OutLocation if successful this variable will be filed with result * @return true if successful, false otherwise */ NAVIGATIONSYSTEM_API virtual bool ProjectPoint(const FVector& Point, FNavLocation& OutLocation, const FVector& Extent, FSharedConstNavQueryFilter Filter = NULL, const UObject* Querier = NULL) const PURE_VIRTUAL(ANavigationData::ProjectPoint, return false;); /** batches ProjectPoint's work for efficiency */ NAVIGATIONSYSTEM_API virtual void BatchProjectPoints(TArray& Workload, const FVector& Extent, FSharedConstNavQueryFilter Filter = NULL, const UObject* Querier = NULL) const PURE_VIRTUAL(ANavigationData::BatchProjectPoints, ); /** Project batch of points using shared search filter. This version is not requiring user to pass in Extent, * and is instead relying on FNavigationProjectionWork.ProjectionLimit. * @note function should assert if item's FNavigationProjectionWork.ProjectionLimit is invalid */ NAVIGATIONSYSTEM_API virtual void BatchProjectPoints(TArray& Workload, FSharedConstNavQueryFilter Filter = NULL, const UObject* Querier = NULL) const PURE_VIRTUAL(ANavigationData::BatchProjectPoints, ); /** Calculates path from PathStart to PathEnd and retrieves its cost. * @NOTE this function does not generate string pulled path so the result is an (over-estimated) approximation * @NOTE potentially expensive, so use it with caution */ NAVIGATIONSYSTEM_API virtual ENavigationQueryResult::Type CalcPathCost(const FVector& PathStart, const FVector& PathEnd, FVector::FReal& OutPathCost, FSharedConstNavQueryFilter QueryFilter = NULL, const UObject* Querier = NULL) const PURE_VIRTUAL(ANavigationData::CalcPathCost, return ENavigationQueryResult::Invalid;); /** Calculates path from PathStart to PathEnd and retrieves its length. * @NOTE this function does not generate string pulled path so the result is an (over-estimated) approximation * @NOTE potentially expensive, so use it with caution */ NAVIGATIONSYSTEM_API virtual ENavigationQueryResult::Type CalcPathLength(const FVector& PathStart, const FVector& PathEnd, FVector::FReal& OutPathLength, FSharedConstNavQueryFilter QueryFilter = NULL, const UObject* Querier = NULL) const PURE_VIRTUAL(ANavigationData::CalcPathLength, return ENavigationQueryResult::Invalid;); /** Calculates path from PathStart to PathEnd and retrieves its length. * @NOTE this function does not generate string pulled path so the result is an (over-estimated) approximation * @NOTE potentially expensive, so use it with caution */ NAVIGATIONSYSTEM_API virtual ENavigationQueryResult::Type CalcPathLengthAndCost(const FVector& PathStart, const FVector& PathEnd, FVector::FReal& OutPathLength, FVector::FReal& OutPathCost, FSharedConstNavQueryFilter QueryFilter = NULL, const UObject* Querier = NULL) const PURE_VIRTUAL(ANavigationData::CalcPathLengthAndCost, return ENavigationQueryResult::Invalid;); /** Checks if specified navigation node contains given location * @param Location is expressed in WorldSpace, navigation data is responsible for tansforming if need be */ NAVIGATIONSYSTEM_API virtual bool DoesNodeContainLocation(NavNodeRef NodeRef, const FVector& WorldSpaceLocation) const PURE_VIRTUAL(ANavigationData::DoesNodeContainLocation, return false;); NAVIGATIONSYSTEM_API double GetWorldTimeStamp() const; //----------------------------------------------------------------------// // Areas //----------------------------------------------------------------------// /** new area was registered in navigation system */ NAVIGATIONSYSTEM_API virtual void OnNavAreaAdded(const UClass* NavAreaClass, int32 AgentIndex); /** area was removed from navigation system */ NAVIGATIONSYSTEM_API virtual void OnNavAreaRemoved(const UClass* NavAreaClass); /** called after changes to registered area classes */ NAVIGATIONSYSTEM_API virtual void OnNavAreaChanged(); NAVIGATIONSYSTEM_API void OnNavAreaEvent(const UClass* NavAreaClass, ENavAreaEvent::Type Event); /** add all existing areas */ NAVIGATIONSYSTEM_API void ProcessNavAreas(const TSet& AreaClasses, int32 AgentIndex); /** get class associated with AreaID */ NAVIGATIONSYSTEM_API const UClass* GetAreaClass(int32 AreaID) const; /** check if AreaID was assigned to class (class itself may not be loaded yet!) */ NAVIGATIONSYSTEM_API bool IsAreaAssigned(int32 AreaID) const; /** get ID assigned to AreaClas or -1 when not assigned */ NAVIGATIONSYSTEM_API int32 GetAreaID(const UClass* AreaClass) const; /** get max areas supported by this navigation data */ virtual int32 GetMaxSupportedAreas() const { return MAX_int32; } /** read all supported areas */ void GetSupportedAreas(TArray& Areas) const { Areas = SupportedAreas; } //----------------------------------------------------------------------// // Custom navigation links //----------------------------------------------------------------------// NAVIGATIONSYSTEM_API virtual void UpdateCustomLink(const INavLinkCustomInterface* CustomLink); //----------------------------------------------------------------------// // Filters //----------------------------------------------------------------------// /** get cached query filter */ NAVIGATIONSYSTEM_API FSharedConstNavQueryFilter GetQueryFilter(TSubclassOf FilterClass) const; /** store cached query filter */ NAVIGATIONSYSTEM_API void StoreQueryFilter(TSubclassOf FilterClass, FSharedConstNavQueryFilter NavFilter); /** removes cached query filter */ NAVIGATIONSYSTEM_API void RemoveQueryFilter(TSubclassOf FilterClass); //----------------------------------------------------------------------// // all the rest //----------------------------------------------------------------------// virtual UPrimitiveComponent* ConstructRenderingComponent() { return NULL; } /** updates state of rendering component */ NAVIGATIONSYSTEM_API void SetNavRenderingEnabled(bool bEnable); #if WITH_EDITOR virtual bool CanChangeIsSpatiallyLoadedFlag() const override { return false; } #endif protected: NAVIGATIONSYSTEM_API void InstantiateAndRegisterRenderingComponent(); /** get ID to assign for newly added area */ NAVIGATIONSYSTEM_API virtual int32 GetNewAreaID(const UClass* AreaClass) const; protected: /** Navigation data versioning. */ UPROPERTY() uint32 DataVersion; typedef FPathFindingResult (*FFindPathPtr)(const FNavAgentProperties& AgentProperties, const FPathFindingQuery& Query); FFindPathPtr FindPathImplementation; FFindPathPtr FindHierarchicalPathImplementation; typedef bool (*FTestPathPtr)(const FNavAgentProperties& AgentProperties, const FPathFindingQuery& Query, int32* NumVisitedNodes); FTestPathPtr TestPathImplementation; FTestPathPtr TestHierarchicalPathImplementation; typedef bool(*FNavRaycastPtr)(const ANavigationData* NavDataInstance, const FVector& RayStart, const FVector& RayEnd, FVector& HitLocation, FSharedConstNavQueryFilter QueryFilter, const UObject* Querier); UE_DEPRECATED(5.6, "Please use RaycastImplementationWithAdditionalResults instead") FNavRaycastPtr RaycastImplementation; typedef bool(*FNavRaycastWithAdditionalResultsPtr)(const ANavigationData* NavDataInstance, const FVector& RayStart, const FVector& RayEnd, FVector& HitLocation, FNavigationRaycastAdditionalResults* AdditionalResults, FSharedConstNavQueryFilter QueryFilter, const UObject* Querier); FNavRaycastWithAdditionalResultsPtr RaycastImplementationWithAdditionalResults; protected: TSharedPtr NavDataGenerator; /** caches requests to rebuild dirty areas while nav rebuilding is suspended * via SetRebuildingSuspended(true) call. Calling SetRebuildingSuspended(false) * will result in pushing SuspendedDirtyAreas contents to the nav generator * and clearing out of SuspendedDirtyAreas */ TArray SuspendedDirtyAreas; /** * Container for all path objects generated with this Navigation Data instance. * Is meant to be added to only on GameThread, and in fact should user should never * add items to it manually, @see CreatePathInstance */ TArray ActivePaths; /** Synchronization object for paths registration from main thread and async pathfinding thread */ mutable FTransactionallySafeCriticalSection ActivePathsLock; /** * Contains paths that requested observing its goal's location. These paths will be * processed on a regular basis (@see ObservedPathsTickInterval) */ TArray ObservedPaths; /** paths that requested re-calculation */ TArray RepathRequests; /** contains how much time left to the next ObservedPaths processing */ float NextObservedPathsTickInSeconds; /** Query filter used when no other has been passed to relevant functions */ FSharedNavQueryFilter DefaultQueryFilter; /** Map of query filters by UNavigationQueryFilter class */ TMap QueryFilters; /** serialized area class - ID mapping */ UPROPERTY() TArray SupportedAreas; /** mapping for SupportedAreas */ TMap AreaClassToIdMap; /** whether this instance is registered with Navigation System*/ uint32 bRegistered : 1; /** was it generated for default agent (SupportedAgents[0]) */ uint32 bSupportsDefaultAgent : 1; /** Set via SetRebuildingSuspended and controlling if RebuildDirtyAreas get * passed over to the generator instantly or cached in SuspendedDirtyAreas * to be applied at later date with SetRebuildingSuspended(false) call */ uint32 bRebuildingSuspended : 1; /** Becomes true when too many dirty areas have been accumulated while rebuild * was suspended (see SetRebuildingSuspended). * If rebuilding is resumed after reaching the max count, this entire navigation * data will be rebuilt. */ uint32 bReachedSuspendedAreasMaxCount : 1 = false; #if WITH_EDITORONLY_DATA uint32 bIsBuildingOnLoad : 1; #endif private: uint16 NavDataUniqueID; static NAVIGATIONSYSTEM_API uint16 GetNextUniqueID(); protected: /** The exact nav agent properties used when registering this navdata * in the navigation system. If navdata configuration changes are * needed, NavDataConfig can be modified and used */ FNavAgentProperties NavAgentProperties; void SetNavAgentProperties(const FNavAgentProperties& InNavAgentProperties) { NavAgentProperties = InNavAgentProperties; } }; struct FAsyncPathFindingQuery : public FPathFindingQuery { const uint32 QueryID; const FNavPathQueryDelegate OnDoneDelegate; const TEnumAsByte Mode; FPathFindingResult Result; FAsyncPathFindingQuery() : QueryID(INVALID_NAVQUERYID) , Mode(EPathFindingMode::Regular) { } FAsyncPathFindingQuery(const UObject* InOwner, const ANavigationData& InNavData, const FVector& Start, const FVector& End, const FNavPathQueryDelegate& Delegate, FSharedConstNavQueryFilter SourceQueryFilter, const FVector::FReal CostLimit = TNumericLimits::Max()); FAsyncPathFindingQuery(const FPathFindingQuery& Query, const FNavPathQueryDelegate& Delegate, const EPathFindingMode::Type QueryMode); protected: FORCEINLINE static uint32 GetUniqueID() { return ++LastPathFindingUniqueID; } static uint32 LastPathFindingUniqueID; }; FORCEINLINE bool FPathFindingResult::IsPartial() const { return (Result != ENavigationQueryResult::Error) && Path.IsValid() && Path->IsPartial(); } FORCEINLINE void FNavigationPath::SetNavigationDataUsed(const ANavigationData* const NavData) { NavigationDataUsed = MakeWeakObjectPtr(const_cast(NavData)); }