├── .gitattributes
├── .gitignore
├── Binaries
├── Linux
│ └── libUE4-DonAINavigation-Linux-Shipping.a
├── Win32
│ └── UE4-DonAINavigation-Win32-Shipping.lib
└── Win64
│ ├── UE4-DonAINavigation-Win64-Shipping.lib
│ ├── UE4Editor-DonAINavigation.dll
│ ├── UE4Editor-DonAINavigation.pdb
│ └── UE4Editor.modules
├── Config
└── FilterPlugin.ini
├── Content
└── Icons
│ └── Navigation_Volumer_Aerial.uasset
├── DonAINavigation.uplugin
├── LICENSE.md
├── README.md
├── Resources
└── Icon128.png
└── Source
└── DonAINavigation
├── Classes
├── BehaviorTree
│ └── BTTask_FlyTo.h
├── DonNavigationCommon.h
├── DonNavigationHelper.h
├── DonNavigationManager.h
├── DonNavigationManagerUnbound.h
├── DonNavigatorInterface.h
└── Multithreading
│ ├── DonDrawDebugThreadSafe.h
│ └── DonNavigationWorker.h
├── DonAINavigation.Build.cs
├── Private
├── BehaviorTree
│ └── BTTask_FlyTo.cpp
├── DonAINavigation.cpp
├── DonAINavigationPrivatePCH.h
├── DonNavigationHelper.cpp
├── DonNavigationManager.cpp
├── DonNavigationManagerUnbound.cpp
├── DonNavigatorInterface.cpp
├── EnvironmentQuery
│ └── DonEnvQueryTest_Navigation.cpp
└── Multithreading
│ └── DonNavigationWorker.cpp
└── Public
├── EnvironmentQuery
└── DonEnvQueryTest_Navigation.h
└── IDonAINavigation.h
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # Windows shortcuts
18 | *.lnk
19 |
20 | # =========================
21 | # Operating System Files
22 | # =========================
23 |
24 | # OSX
25 | # =========================
26 |
27 | .DS_Store
28 | .AppleDouble
29 | .LSOverride
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear in the root of a volume
35 | .DocumentRevisions-V100
36 | .fseventsd
37 | .Spotlight-V100
38 | .TemporaryItems
39 | .Trashes
40 | .VolumeIcon.icns
41 |
42 | # Directories potentially created on remote AFP share
43 | .AppleDB
44 | .AppleDesktop
45 | Network Trash Folder
46 | Temporary Items
47 | .apdisk
48 |
--------------------------------------------------------------------------------
/Binaries/Linux/libUE4-DonAINavigation-Linux-Shipping.a:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VSZue/DonAINavigation/96241fb3714f4cc9f4b1c6ece382efce3f3f0c59/Binaries/Linux/libUE4-DonAINavigation-Linux-Shipping.a
--------------------------------------------------------------------------------
/Binaries/Win32/UE4-DonAINavigation-Win32-Shipping.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VSZue/DonAINavigation/96241fb3714f4cc9f4b1c6ece382efce3f3f0c59/Binaries/Win32/UE4-DonAINavigation-Win32-Shipping.lib
--------------------------------------------------------------------------------
/Binaries/Win64/UE4-DonAINavigation-Win64-Shipping.lib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VSZue/DonAINavigation/96241fb3714f4cc9f4b1c6ece382efce3f3f0c59/Binaries/Win64/UE4-DonAINavigation-Win64-Shipping.lib
--------------------------------------------------------------------------------
/Binaries/Win64/UE4Editor-DonAINavigation.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VSZue/DonAINavigation/96241fb3714f4cc9f4b1c6ece382efce3f3f0c59/Binaries/Win64/UE4Editor-DonAINavigation.dll
--------------------------------------------------------------------------------
/Binaries/Win64/UE4Editor-DonAINavigation.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VSZue/DonAINavigation/96241fb3714f4cc9f4b1c6ece382efce3f3f0c59/Binaries/Win64/UE4Editor-DonAINavigation.pdb
--------------------------------------------------------------------------------
/Binaries/Win64/UE4Editor.modules:
--------------------------------------------------------------------------------
1 | {
2 | "BuildId": "3944462",
3 | "Modules":
4 | {
5 | "DonAINavigation": "UE4Editor-DonAINavigation.dll"
6 | }
7 | }
--------------------------------------------------------------------------------
/Config/FilterPlugin.ini:
--------------------------------------------------------------------------------
1 | [FilterPlugin]
2 | ; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
3 | ; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
4 | ;
5 | ; Examples:
6 | ; /README.txt
7 | ; /Extras/...
8 | ; /Binaries/ThirdParty/*.dll
9 |
--------------------------------------------------------------------------------
/Content/Icons/Navigation_Volumer_Aerial.uasset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VSZue/DonAINavigation/96241fb3714f4cc9f4b1c6ece382efce3f3f0c59/Content/Icons/Navigation_Volumer_Aerial.uasset
--------------------------------------------------------------------------------
/DonAINavigation.uplugin:
--------------------------------------------------------------------------------
1 | {
2 | "FileVersion" : 3,
3 | "FriendlyName" : "DoN AI Navigation Plugin (Flying AI /3D Aerial Pathfinding)",
4 | "Version" : 10,
5 | "VersionName" : "1.10",
6 | "EngineVersion" : "4.19.0",
7 | "Category" : "AI Navigation",
8 | "CreatedBy" : "Venugopalan Sreedharan",
9 | "CreatedByURL" : "http://www.drunkonnectar.com/3d-pathfinding-ue4/",
10 | "Description" : "This plugin is a solution for 3D pathfinding for flying creatures.
11 |
12 | It uses voxel based navigation, supports dynamic collision updates and comes with a behavior tree task 'Fly to' which you may directly use in your behavior trees.
13 |
14 | Also provided is an API with pathfinding functions for custom navigation queries",
15 | "MarketplaceURL" : "com.epicgames.launcher://ue/marketplace/content/81b4bd085c8c41508edfeb160b6ddf62",
16 | "SupportURL" : "https://forums.unrealengine.com/showthread.php?102933-DoN-s-3D-Pathfinding-Flying-AI-system-(with-full-source!)/",
17 | "DocsURL" : "https://www.youtube.com/watch?v=6Tr_K551zvI",
18 | "CanContainContent" : true,
19 | "EnabledByDefault" : true,
20 | "Modules" :
21 | [
22 | {
23 | "Name" : "DonAINavigation",
24 | "Type" : "Runtime",
25 | "LoadingPhase" : "PreDefault",
26 | "WhitelistPlatforms" :
27 | [
28 | "Win64",
29 | "Win32",
30 | "Linux"
31 | ]
32 | }
33 | ]
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Venugopalan Sreedharan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | =============================================
2 | DoN AI Navigation Plugin for Unreal Engine 4
3 | =============================================
4 |
5 | This plugin provides a 3D dynamic pathfinding system for use with Unreal Engine 4. This system was first developed for my game [DoN The Nature Game](http://www.drunkonnectar.com/) and I decided to make a plugin out of the navigation module as a gift to the Unreal community.
6 |
7 | It's primarly designed for Flying AI creatures based in dynamic or procedural worlds that need to solve complex pathfinding tasks and other behavior that cannot be readily solved by Unreal's native AI navigation system and also for maps that are too complex to be solved by simple ray-tracing/sweeping heuristics or by waypoint systems.
8 |
9 | Where possible, I recommend that you use Unreal's native AI navigation or simpler navigation strategies; use this system when the more conventional strategies cannot solve your pathfinding usecases.
10 |
11 | The plugin provides the following:
12 | * Navigation Manager actor for configuring the system
13 | * "Fly To" behavior tree node that can be readily dropped into your behavior trees
14 | * Nodes for managing dynamic collision and pathfinding in your scene.
15 | * A pathfinding API that advanced users can use for custom navigation queries from either Blueprints or C++
16 |
17 | =============================================
18 | Download Sample Project
19 | =============================================
20 | I've created a sample project for you to quickly test the system and understand the different usecases it covers.
21 |
22 | Please visit the following page to grab it:
23 |
24 | http://www.drunkonnectar.com/3d-pathfinding-ue4/
25 |
26 | =============================================
27 | Overview and Tutorial
28 | =============================================
29 | [](https://www.youtube.com/watch?v=6Tr_K551zvI)
30 |
31 | =============================================
32 | Technical Overview
33 | =============================================
34 | For a technical overview of the project, please visit this link:
35 | http://www.drunkonnectar.com/3d-pathfinding-ue4/#TechnicalOverview
36 |
37 |
--------------------------------------------------------------------------------
/Resources/Icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VSZue/DonAINavigation/96241fb3714f4cc9f4b1c6ece382efce3f3f0c59/Resources/Icon128.png
--------------------------------------------------------------------------------
/Source/DonAINavigation/Classes/BehaviorTree/BTTask_FlyTo.h:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | #pragma once
16 |
17 | #include "BehaviorTree/Tasks/BTTask_BlackboardBase.h"
18 |
19 | #include "DonNavigationHelper.h"
20 |
21 | #include "BTTask_FlyTo.generated.h"
22 |
23 | USTRUCT()
24 | struct FBT_FlyToTarget_DebugParams : public FDoNNavigationDebugParams
25 | {
26 | GENERATED_USTRUCT_BODY()
27 |
28 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
29 | bool bVisualizePawnAsVoxels;
30 | };
31 |
32 |
33 | struct FBT_FlyToTarget_Metadata
34 | {
35 | uint16 ActiveInstanceIdx;
36 |
37 | TWeakObjectPtr OwnerComp;
38 |
39 | FBT_FlyToTarget_Metadata(){}
40 |
41 | FBT_FlyToTarget_Metadata(uint16 ActiveInstanceIdx, UBehaviorTreeComponent* OwnerComp) : ActiveInstanceIdx(ActiveInstanceIdx), OwnerComp(OwnerComp){}
42 |
43 | };
44 |
45 |
46 | struct FBT_FlyToTarget
47 | {
48 | FBT_FlyToTarget_Metadata Metadata;
49 |
50 | FDoNNavigationQueryParams QueryParams;
51 |
52 | FDonNavigationDynamicCollisionDelegate DynamicCollisionListener;
53 |
54 | int32 solutionTraversalIndex = 0;
55 |
56 | FDoNNavigationQueryData QueryResults;
57 |
58 | bool bSolutionInvalidatedByDynamicObstacle = false;
59 |
60 | bool bIsANavigator = false;
61 |
62 | FVector TargetLocation;
63 |
64 | FDelegateHandle BBObserverDelegateHandle;
65 |
66 | uint32 bTargetLocationChanged : 1;
67 |
68 | void Reset()
69 | {
70 | solutionTraversalIndex = 0;
71 | QueryResults = FDoNNavigationQueryData();
72 | QueryParams = FDoNNavigationQueryParams();
73 | Metadata = FBT_FlyToTarget_Metadata();
74 | bSolutionInvalidatedByDynamicObstacle = false;
75 | bTargetLocationChanged = false;
76 | }
77 | };
78 |
79 | /**
80 | *
81 | */
82 | UCLASS()
83 | class UBTTask_FlyTo : public UBTTaskNode
84 | {
85 | GENERATED_BODY()
86 |
87 | public:
88 | UBTTask_FlyTo(const FObjectInitializer& ObjectInitializer);
89 |
90 | virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
91 | virtual EBTNodeResult::Type AbortTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
92 | virtual uint16 GetInstanceMemorySize() const override;
93 | virtual void DescribeRuntimeValues(const UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTDescriptionVerbosity::Type Verbosity, TArray& Values) const override;
94 | virtual FString GetStaticDescription() const override;
95 | virtual void InitializeFromAsset(UBehaviorTree& Asset) override;
96 |
97 | EBTNodeResult::Type HandleTaskFailure(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, UBlackboardComponent* Blackboard);
98 | void HandleTaskFailureAndExit(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory);
99 |
100 | EBlackboardNotificationResult OnBlackboardValueChange(const UBlackboardComponent& Blackboard, FBlackboard::FKey ChangedKeyID);
101 |
102 | #if WITH_EDITOR
103 | virtual FName GetNodeIconName() const override;
104 | #endif // WITH_EDITOR
105 |
106 | // Behavior Tree Input:
107 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
108 | FBlackboardKeySelector FlightLocationKey;
109 |
110 | /* Optional: Useful in somecases where you want failure or success of a task to automatically update a particular blackboard key*/
111 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
112 | FBlackboardKeySelector FlightResultKey;
113 |
114 | /* Optional: This boolean will be flip-flopped at the end of this task (regardless of success or failure). This can be useful for certain types of behavior tree setups*/
115 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
116 | FBlackboardKeySelector KeyToFlipFlopWhenTaskExits;
117 |
118 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
119 | float MinimumProximityRequired = 15.f;
120 |
121 | // Venu's Note:- Code for repath tolerance below was kindly contributed by Vladimir Ivanov (Github @ArCorvus). Thank you Vladimir!
122 | //
123 | /** Recalculate path enable */
124 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DoN Navigation", meta = (InlineEditConditionToggle))
125 | uint32 bRecalcPathOnDestinationChanged : 1;
126 |
127 | /** Recalculate path if FlightLocation value changed. */
128 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "DoN Navigation", meta = (EditCondition = "bRecalcPathOnDestinationChanged"))
129 | float RecalculatePathTolerance;
130 | //
131 |
132 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
133 | FDoNNavigationQueryParams QueryParams;
134 |
135 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
136 | FBT_FlyToTarget_DebugParams DebugParams;
137 |
138 | UFUNCTION(BlueprintCallable, Category="DoN Navigation")
139 | void Pathfinding_OnFinish(const FDoNNavigationQueryData& Data);
140 |
141 | UFUNCTION(BlueprintCallable, Category="DoN Navigation")
142 | void Pathfinding_OnDynamicCollisionAlert(const FDonNavigationDynamicCollisionPayload& Data);
143 |
144 | UPROPERTY(BlueprintReadOnly, Category = "DoN Navigation")
145 | ADonNavigationManager* NavigationManager;
146 |
147 | /** In some scenarios, it may be desirable to allow the A.I. to teleport to its intended destination if pathfinding failed for some reason
148 | * Set this flag to true to if you want this behavior*/
149 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
150 | bool bTeleportToDestinationUponFailure = false;
151 |
152 | // Makeshift arrangement until the Task Owner / Task List discrepancy bug is comprehensively conquered
153 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
154 | float MaxTimeBeforeTeleport = 10.f;
155 |
156 | protected:
157 |
158 | virtual void TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
159 |
160 | FBT_FlyToTarget* TaskMemoryFromGenericPayload(void* GenericPayload);
161 |
162 | EBTNodeResult::Type SchedulePathfindingRequest(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory);
163 |
164 | void AbortPathfindingRequest(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory);
165 |
166 | void TickPathNavigation(UBehaviorTreeComponent& OwnerComp, FBT_FlyToTarget* MyMemory, float DeltaSeconds);
167 |
168 | virtual void OnTaskFinished(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTNodeResult::Type TaskResult) override;
169 |
170 | virtual bool TeleportAndExit(UBehaviorTreeComponent& OwnerComp, bool bWrapUpLatentTask = true);
171 |
172 | //
173 | TMap, float> LastRequestTimestamps;
174 | //float LastRequestTimestamp;
175 | const static float RequestThrottleInterval;
176 | };
--------------------------------------------------------------------------------
/Source/DonAINavigation/Classes/DonNavigationCommon.h:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | #pragma once
16 |
17 | #include
18 | #include
19 | #include
20 |
21 | #if PLATFORM_WINDOWS
22 | #include "AllowWindowsPlatformTypes.h"
23 | #elif PLATFORM_XBOXONE
24 | #include "XboxOneAllowPlatformTypes.h"
25 | #endif
26 |
27 |
28 | namespace DoNNavigation
29 | {
30 | // Priority Queue for the pathfinding algorithm to use:
31 | template
32 | struct PriorityQueue {
33 | typedef std::pair PQElement;
34 | std::priority_queue, std::greater> elements;
35 |
36 | inline bool empty() { return elements.empty(); }
37 |
38 | inline void put(T item, Number priority) {
39 | elements.emplace(priority, item);
40 | }
41 |
42 | inline T get() {
43 | T best_item = elements.top().second;
44 | elements.pop();
45 | return best_item;
46 | }
47 | };
48 |
49 | // Debug timer functions for profiling parts of the plugin that aren't easily profiled via Unreal's profiler
50 | // Eg: For profiling initial collision sampling on map load, etc
51 | static FORCEINLINE uint64 Debug_GetTimeMs64()
52 | {
53 | #if PLATFORM_WINDOWS || PLATFORM_XBOXONE
54 | /* Windows */
55 | FILETIME ft;
56 | LARGE_INTEGER li;
57 |
58 | /* Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
59 | * to a LARGE_INTEGER structure. */
60 | GetSystemTimeAsFileTime(&ft);
61 | li.LowPart = ft.dwLowDateTime;
62 | li.HighPart = ft.dwHighDateTime;
63 |
64 | uint64 ret = li.QuadPart;
65 | ret -= 116444736000000000LL; /* Convert from file time to UNIX epoch time. */
66 | ret /= 10000; /* From 100 nano seconds (10^-7) to 1 millisecond (10^-3) intervals */
67 |
68 | return ret;
69 | #else
70 | /* Linux */
71 | struct timeval tv;
72 |
73 | gettimeofday(&tv, NULL);
74 |
75 | uint64 ret = tv.tv_usec;
76 | /* Convert from micro seconds (10^-6) to milliseconds (10^-3) */
77 | ret /= 1000;
78 |
79 | /* Adds the seconds (10^0) after converting them to milliseconds (10^-3) */
80 | ret += (tv.tv_sec * 1000);
81 |
82 | return ret;
83 | #endif
84 | }
85 |
86 | uint64 FORCEINLINE Debug_GetTimer()
87 | {
88 | return Debug_GetTimeMs64();
89 | }
90 |
91 | void FORCEINLINE Debug_StopTimer(uint64& timer)
92 | {
93 | timer = Debug_GetTimeMs64() - timer;
94 | }
95 | }
96 |
97 | #if PLATFORM_WINDOWS
98 | #include "HideWindowsPlatformTypes.h"
99 | #endif
--------------------------------------------------------------------------------
/Source/DonAINavigation/Classes/DonNavigationHelper.h:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | #pragma once
16 |
17 | #include "Kismet/BlueprintFunctionLibrary.h"
18 | #include "DonNavigationManager.h"
19 |
20 | #include "DonNavigationHelper.generated.h"
21 |
22 | /**
23 | *
24 | */
25 | UCLASS()
26 | class DONAINAVIGATION_API UDonNavigationHelper : public UBlueprintFunctionLibrary
27 | {
28 | GENERATED_BODY()
29 |
30 | public:
31 |
32 | /* Returns the Voxel navigation builder used for building navigation volumes and performing pathfinding */
33 | UFUNCTION(BlueprintPure, Category = "DoN Navigation", meta = (WorldContext = "WorldContextObject"))
34 | static ADonNavigationManager* DonNavigationManager(UObject* WorldContextObject);
35 |
36 | /* Returns the Voxel navigation builder used for building navigation volumes and performing pathfinding for specified Actor*/
37 | UFUNCTION(BlueprintPure, Category = "DoN Navigation")
38 | static ADonNavigationManager* DonNavigationManagerForActor(const AActor *Actor);
39 | };
40 |
--------------------------------------------------------------------------------
/Source/DonAINavigation/Classes/DonNavigationManager.h:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | #pragma once
16 |
17 | #include "DonNavigationCommon.h"
18 | #include "Multithreading/DonDrawDebugThreadSafe.h"
19 | #include "CollisionQueryParams.h"
20 | #include "WorldCollision.h"
21 | #include "Queue.h"
22 | #include "Components/BoxComponent.h"
23 |
24 | #include "DonNavigationManager.generated.h"
25 |
26 | DECLARE_STATS_GROUP(TEXT("DonNavigation"), STATGROUP_DonNavigation, STATCAT_Advanced);
27 |
28 | UENUM()
29 | enum class EDonNavigationQueryStatus : uint8
30 | {
31 | Unscheduled,
32 | InProgress,
33 | Success,
34 | Failure,
35 | QueryHasNoSolution,
36 | TimedOut
37 | };
38 |
39 | struct FDonNavigationDynamicCollisionNotifyee;
40 |
41 | /**
42 | * This is the basic unit of pathfinding for Finite Worlds.
43 | * Infinite Worlds (Unbound Manager) rely directly on FVectors
44 | */
45 | USTRUCT()
46 | struct FDonNavigationVoxel
47 | {
48 | GENERATED_USTRUCT_BODY()
49 |
50 | int32 X;
51 | int32 Y;
52 | int32 Z;
53 |
54 | FVector Location;
55 | uint8 NumResidents = 0;
56 | bool bIsInitialized = false;
57 |
58 | TArray DynamicCollisionNotifyees;
59 |
60 | bool FORCEINLINE CanNavigate() { return NumResidents == 0; }
61 |
62 | void SetNavigability(bool CanNavigate)
63 | {
64 | if (!CanNavigate)
65 | NumResidents++;
66 | else
67 | NumResidents = NumResidents > 0 ? NumResidents - 1 : 0;
68 | }
69 |
70 | void BroadcastCollisionUpdates();
71 |
72 | friend bool operator== (const FDonNavigationVoxel& A, const FDonNavigationVoxel& B)
73 | {
74 | return A.X == B.X && A.Y == B.Y && A.Z == B.Z;
75 | }
76 |
77 | FDonNavigationVoxel(){}
78 | };
79 |
80 | /*
81 | * Approximates the collision geometry of a mesh in the form of voxels.
82 | * This is only used when a pawn's collision body exceeds exceeds a "unit voxel volume" for the world.
83 | * For maximum performance it is recommended to tune the manager's VoxelSize to a value that roughly approximates your pawn's collision body.
84 | * If that is done correctly, this struct is never used as all collision checks for the pawn are inferred directly around its origin.
85 | */
86 | USTRUCT()
87 | struct FDonVoxelCollisionProfile
88 | {
89 | GENERATED_USTRUCT_BODY()
90 |
91 | TArray RelativeVoxelOccupancy;
92 |
93 | /**
94 | * Note:- These references are only valid so long as NAVVolumeData (see ADonNavigationManager) is not modified in any way.
95 | * Presently, NAVVolumeData is populated once and only once (at the beginning of the game) and with this model, the references are safe to rely upon.
96 | */
97 |
98 | TArray WorldVoxelsOccupied;
99 |
100 | };
101 |
102 | /**
103 | * Collision payloads are any generic set of data passed from an external source (eg: The Behavior Tree "Fly To" node provided with this plugin) to the manager.
104 | * They are passed back via callbacks to anyone listening to the manager's query results, dynamic collision updates and other such callbacks.
105 | */
106 | USTRUCT(BlueprintType)
107 | struct FDonNavigationDynamicCollisionPayload
108 | {
109 | GENERATED_USTRUCT_BODY()
110 |
111 | void* CustomDelegatePayload;
112 |
113 | FDonNavigationVoxel Voxel;
114 |
115 | FDonNavigationDynamicCollisionPayload(){}
116 |
117 | FDonNavigationDynamicCollisionPayload(void* CustomDelegatePayloadIn, FDonNavigationVoxel VoxelIn) : CustomDelegatePayload(CustomDelegatePayloadIn), Voxel(VoxelIn) {}
118 | };
119 |
120 | DECLARE_DYNAMIC_DELEGATE_OneParam(FDonNavigationDynamicCollisionDelegate, const FDonNavigationDynamicCollisionPayload&, Data); // note: non-dynamic delegate can't be used as a function parameter apparently
121 |
122 | /* This Collision Notifyee struct contains the delegate for any class listening to dynamic collisions and the paylaod provided by that class */
123 | USTRUCT()
124 | struct FDonNavigationDynamicCollisionNotifyee
125 | {
126 | GENERATED_USTRUCT_BODY()
127 |
128 | UPROPERTY()
129 | FDonNavigationDynamicCollisionDelegate Listener;
130 |
131 | FDonNavigationDynamicCollisionPayload Payload;
132 |
133 | FDonNavigationDynamicCollisionNotifyee(){}
134 |
135 | FDonNavigationDynamicCollisionNotifyee(FDonNavigationDynamicCollisionDelegate ListenerIn, FDonNavigationDynamicCollisionPayload PayloadIn) : Listener(ListenerIn), Payload(PayloadIn)
136 | {
137 |
138 | }
139 |
140 | friend bool operator== (const FDonNavigationDynamicCollisionNotifyee& A, const FDonNavigationDynamicCollisionNotifyee& B)
141 | {
142 | return A.Listener == B.Listener;
143 | }
144 | };
145 |
146 | // Finite World data structure:
147 | // Nested Structs for aggregating world voxels across three axes:
148 | USTRUCT()
149 | struct FDonNavVoxelY
150 | {
151 | GENERATED_USTRUCT_BODY()
152 |
153 | UPROPERTY()
154 | TArray Z;
155 |
156 | void AddZ(FDonNavigationVoxel volume)
157 | {
158 | Z.Add(volume);
159 | }
160 |
161 | FDonNavVoxelY()
162 | {
163 | }
164 | };
165 |
166 | USTRUCT()
167 | struct FDonNavVoxelX
168 | {
169 | GENERATED_USTRUCT_BODY()
170 |
171 | UPROPERTY()
172 | TArray Y;
173 |
174 | void AddY(FDonNavVoxelY YPlaneVolume)
175 | {
176 | Y.Add(YPlaneVolume);
177 | }
178 |
179 | FDonNavVoxelX()
180 | {
181 | }
182 | };
183 |
184 | USTRUCT()
185 | struct FDonNavVoxelXYZ
186 | {
187 | GENERATED_USTRUCT_BODY()
188 |
189 | UPROPERTY()
190 | TArray X;
191 |
192 | void AddX(FDonNavVoxelX XPlaneVolume)
193 | {
194 | X.Add(XPlaneVolume);
195 | }
196 |
197 | void ClearAll()
198 | {
199 | for (FDonNavVoxelX _x : X)
200 | {
201 | for (FDonNavVoxelY _y : _x.Y)
202 | _y.Z.Empty();
203 |
204 | _x.Y.Empty();
205 | }
206 |
207 | X.Empty();
208 | }
209 |
210 | FDonNavVoxelXYZ()
211 | {
212 | }
213 | };
214 |
215 | /**
216 | * These parameters are passed by end users (via direct API calls or via the "Fly To" Behavior Tree node) to customize various aspects of this pathfinding system
217 | */
218 | USTRUCT(BlueprintType)
219 | struct FDoNNavigationQueryParams
220 | {
221 | GENERATED_USTRUCT_BODY()
222 |
223 | /** If a query takes more time to run than the value specified here, the pathfinding task will abort
224 | * This is useful to prevent expensive queries (eg: by passing a destination for which no solution exists)
225 | * from clogging up the pathfinding system
226 | */
227 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DoN Navigation")
228 | float QueryTimeout = 3.f;
229 |
230 | /*
231 | * If enabled, your A.I.'s origin or destination will be slightly nudged to accommodate tricky scenarios where
232 | * your A.I. needs to start or finish its pathfinding flush with a collision body (eg: hiding right next to a wall)
233 | * The magnitude of nudging can be configured directly in the Navigation Manager's AutoCorrectionGuessList variable
234 | */
235 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DoN Navigation")
236 | bool bFlexibleOriginGoal = true;
237 |
238 | /** Skips optimization of the path solution completely. Optimized paths are shorter and more visually cogent
239 | * but come at a cost. Maps with low voxel density (high VoxelSize value) usually need optimization for best results
240 | */
241 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DoN Navigation")
242 | bool bSkipOptimizationPass = false;
243 |
244 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DoN Navigation")
245 | int32 MaxOptimizerSweepAttemptsPerNode = 25;
246 |
247 | /** Enabling this will sample all voxels of your pawn or character for determining whether a path solution
248 | * needs to be recalculated due to dynamic obstacles. This will improve the accuracy of response to dynamic collisions
249 | * but comes at a steep cost as the number of event delegates required for listening to precise dynamic collisions is high
250 | * Only use this if you really need to.*/
251 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DoN Navigation")
252 | bool bPreciseDynamicCollisionRepathing = false;
253 |
254 | /** If the destination can be reached via straight line travel, then skip creation of dynamic collision listeners and repathing
255 | * to maximize performance. However, advanced usecases (like the sample project's over-the-top multi-rocket dodging example)
256 | * may need dynamic repathing even for straight-line travel. In such a case you should set this variable to false
257 | */
258 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DoN Navigation")
259 | bool bIgnoreDynamicCollisionRepathingForDirectGoals = true;
260 |
261 | /** Allows you to inflate your mesh's collision extents by a fixed increment for all sweep based testing.
262 | * Typically used if you find your mesh bumping into obstacles while navigating along path solutions.
263 | */
264 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DoN Navigation")
265 | float CollisionShapeInflation = 0.f;
266 |
267 | /** By default the navigation manager will not allow you to schedule a new query if a pre-existing query
268 | * scheduled by you is not yet complete. You can override this behavior by forcing the manager to abort
269 | * the old query and reschedule this one. Use with discretion: Hammering the manager with queries can
270 | * quickly degrade performance (especially in a dynamic collision context).
271 | */
272 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DoN Navigation")
273 | bool bForceRescheduleQuery = false;
274 |
275 | /** Generic pointer allowing you to store anything you like to be passed back as payload.
276 | * Typically used for passing unqiue identifiers in situations where you can't otherwise identify the task owner
277 | * (Eg: Behavior tree singleton nodes)
278 | */
279 | void* CustomDelegatePayload = NULL;
280 |
281 | FDoNNavigationQueryParams()
282 | {
283 |
284 | }
285 | };
286 |
287 | /** Navigation Query Debug Parameters */
288 | USTRUCT(BlueprintType)
289 | struct FDoNNavigationDebugParams
290 | {
291 | GENERATED_USTRUCT_BODY()
292 |
293 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
294 | bool DrawDebugVolumes = false;
295 |
296 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
297 | bool VisualizeRawPath = false;
298 |
299 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
300 | bool VisualizeOptimizedPath = false;
301 |
302 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
303 | bool VisualizeInRealTime = false;
304 |
305 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
306 | float LineThickness = 2.f;
307 |
308 | /* -1 signifies persistent lines that need to be flushed out manually to clear them*/
309 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation")
310 | float LineDuration = -1.f;
311 |
312 | FDoNNavigationDebugParams() {}
313 |
314 | FDoNNavigationDebugParams(bool _DrawDebugVolumes, bool _VisualizeRawPath, bool _VisualizeOptimizedPath, bool _VisualizeInRealTime, float _LineThickness)
315 | {
316 | DrawDebugVolumes = _DrawDebugVolumes;
317 | VisualizeRawPath = _VisualizeRawPath;
318 | VisualizeOptimizedPath = _VisualizeOptimizedPath;
319 | VisualizeInRealTime = _VisualizeInRealTime;
320 | LineThickness = _LineThickness;
321 | }
322 | };
323 |
324 | /** This structure is simply a wrapper around FVector that implements a dummy comparison operator enabling its use with an std::pair driven priority queue*/
325 | struct FDonNavigationLocVector : public FVector
326 | {
327 | FDonNavigationLocVector(){}
328 |
329 | FDonNavigationLocVector(FVector Vector)
330 | {
331 | X = Vector.X;
332 | Y = Vector.Y;
333 | Z = Vector.Z;
334 | }
335 |
336 | friend bool operator< (const FDonNavigationLocVector& A, const FDonNavigationLocVector& B)
337 | {
338 | // This function is simply a workaround for enabling use of FVector type with an std::pair priority queue.
339 | // The value returned is irrelevant because we will be using this as the second element of the std::pair
340 | // where the only first element is actually relevant for comparison.
341 |
342 | return false;
343 | }
344 |
345 | friend uint32 GetTypeHash(const FDonNavigationLocVector& Key) { return FCrc::MemCrc32(&Key, sizeof(Key)); } // similar to FVector's hash, although that uses FCrc::MemCrc_DEPRECATED instead
346 | };
347 |
348 | /**
349 | * Encapsulates all the data relevant for a single navigation query request.
350 | Some variables in this are updated in real-time per tick as the navigation solver sequentially processes each task in its queue
351 | */
352 | USTRUCT(BlueprintType)
353 | struct FDoNNavigationQueryData
354 | {
355 | GENERATED_USTRUCT_BODY()
356 |
357 | // Query input
358 | UPROPERTY(BlueprintReadOnly, Category = "DoN Navigation")
359 | TWeakObjectPtr Actor;
360 |
361 | UPROPERTY()
362 | TWeakObjectPtr CollisionComponent;
363 |
364 | UPROPERTY(BlueprintReadOnly, Category = "DoN Navigation")
365 | FVector Origin;
366 |
367 | UPROPERTY(BlueprintReadOnly, Category = "DoN Navigation")
368 | FVector Destination;
369 |
370 | UPROPERTY(BlueprintReadOnly, Category = "DoN Navigation")
371 | FDoNNavigationQueryParams QueryParams;
372 |
373 | FDoNNavigationDebugParams DebugParams;
374 |
375 | // Unbound:
376 | FVector OriginVolumeCenter;
377 | FVector DestinationVolumeCenter;
378 |
379 | FDonVoxelCollisionProfile VoxelCollisionProfile;
380 |
381 | // Processing state variables
382 | bool bGoalFound = false;
383 | bool bGoalOptimized = false;
384 | FDonNavigationVoxel* OriginVolume;
385 | FDonNavigationVoxel* DestinationVolume;
386 |
387 | DoNNavigation::PriorityQueue Frontier;
388 | TMap VolumeVsCostMap;
389 | TMap VolumeVsGoalTrajectoryMap;
390 |
391 | DoNNavigation::PriorityQueue Frontier_Unbound;
392 | TMap VolumeVsCostMap_Unbound;
393 | TMap VolumeVsGoalTrajectoryMap_Unbound;
394 |
395 | // Optimization state variables
396 | bool bOptimizationInProgress = false;
397 | int32 optimizer_i = 0;
398 | int32 optimizer_j = 0;
399 |
400 | // Iteration Stats
401 | int32 SolverIterationCount = 0;
402 | float SolverTimeTaken = 0.f;
403 |
404 | // Solution
405 | TArray VolumeSolution;
406 | TArray VolumeSolutionOptimized;
407 |
408 | TArray PathSolutionRaw;
409 |
410 | UPROPERTY(BlueprintReadOnly, Category = "DoN Navigation")
411 | TArray PathSolutionOptimized;
412 |
413 | UPROPERTY(BlueprintReadOnly, Category = "DoN Navigation")
414 | EDonNavigationQueryStatus QueryStatus = EDonNavigationQueryStatus::Unscheduled;
415 |
416 |
417 | FDoNNavigationQueryData(){}
418 |
419 | FDoNNavigationQueryData(AActor* ActorIn, UPrimitiveComponent* CollisionComponentIn, FVector OriginIn, FVector DestinationIn, const FDoNNavigationQueryParams& QueryParamsIn,
420 | const FDoNNavigationDebugParams& DebugParamsIn, FDonNavigationVoxel* OriginVolumeIn, FDonNavigationVoxel* DestinationVolumeIn,
421 | FVector OriginVolumeCenterIn, FVector DestinationVolumeCenterIn, FDonVoxelCollisionProfile VoxelCollisionProfileIn)
422 | : Actor(ActorIn), CollisionComponent(CollisionComponentIn), Origin(OriginIn), Destination(DestinationIn), QueryParams(QueryParamsIn), DebugParams(DebugParamsIn),
423 | OriginVolumeCenter(OriginVolumeCenterIn), DestinationVolumeCenter(DestinationVolumeCenterIn), VoxelCollisionProfile(VoxelCollisionProfileIn),
424 | OriginVolume(OriginVolumeIn), DestinationVolume(DestinationVolumeIn)
425 | {}
426 |
427 | FORCEINLINE FString GetActorName() { return Actor.IsValid() ? Actor->GetName() : FString(); }
428 |
429 | void BeginOptimizationCycle()
430 | {
431 | optimizer_i = 0;
432 | optimizer_j = PathSolutionRaw.Num() - 1;
433 |
434 | if (PathSolutionRaw.Num())
435 | {
436 | PathSolutionOptimized.Add(PathSolutionRaw[0]);
437 | VolumeSolutionOptimized.Reserve(PathSolutionRaw.Num());
438 | }
439 |
440 | bOptimizationInProgress = true;
441 | }
442 |
443 | FORCEINLINE bool MaxSweepAttemptsReachedForNode()
444 | {
445 | return PathSolutionRaw.Num() - optimizer_j > QueryParams.MaxOptimizerSweepAttemptsPerNode;
446 | }
447 |
448 | };
449 |
450 | /**
451 | * Result Handler Delegate: This is used by the Navigation Manager for signialling to API callers/BT nodes/etc that a navigation query is complete
452 | * and that the pawn can now consume the path solution to navigate to its goal
453 | */
454 | DECLARE_DYNAMIC_DELEGATE_OneParam(FDoNNavigationResultHandler, const FDoNNavigationQueryData&, Data);
455 |
456 | enum class EDonNavigationRequestType : uint8
457 | {
458 | New,
459 | Abort,
460 | };
461 |
462 | USTRUCT()
463 | struct FDonNavigationTask
464 | {
465 | GENERATED_USTRUCT_BODY()
466 |
467 | virtual void BroadcastResult() {}
468 | virtual ~FDonNavigationTask() {}
469 | };
470 |
471 | /**
472 | * Represents a single navigation task. These are contained in a queue and solved with a fixed number of solver iterations allocated to each task still pending in the queue
473 | */
474 | USTRUCT()
475 | struct FDonNavigationQueryTask : public FDonNavigationTask
476 | {
477 | GENERATED_USTRUCT_BODY()
478 |
479 | EDonNavigationRequestType RequestType;
480 |
481 | FDoNNavigationQueryData Data;
482 |
483 | UPROPERTY()
484 | FDoNNavigationResultHandler ResultHandler;
485 |
486 | UPROPERTY()
487 | FDonNavigationDynamicCollisionDelegate DynamicCollisionListener;
488 |
489 | FDonNavigationQueryTask() { RequestType = EDonNavigationRequestType::New; }
490 | virtual ~FDonNavigationQueryTask() {}
491 |
492 | FDonNavigationQueryTask(AActor* InActor, EDonNavigationRequestType InRequestType) { Data.Actor = InActor; RequestType = InRequestType;}
493 |
494 | FDonNavigationQueryTask(FDoNNavigationQueryData InData, FDoNNavigationResultHandler ResultHandlerIn, FDonNavigationDynamicCollisionDelegate DynamicCollisionNotifierIn)
495 | : Data(InData), ResultHandler(ResultHandlerIn), DynamicCollisionListener(DynamicCollisionNotifierIn)
496 | {
497 | if (!InData.OriginVolume) // Unbound
498 | {
499 | Data.Frontier_Unbound.put(InData.OriginVolumeCenter, 0);
500 | Data.VolumeVsCostMap_Unbound.Add(InData.OriginVolumeCenter, 0);
501 | }
502 | else
503 | {
504 | Data.Frontier.put(InData.OriginVolume, 0);
505 | Data.VolumeVsCostMap.Add(InData.OriginVolume, 0);
506 | }
507 |
508 | Data.QueryStatus = EDonNavigationQueryStatus::InProgress;
509 | RequestType = EDonNavigationRequestType::New;
510 | }
511 |
512 | FORCEINLINE bool IsQueryComplete()
513 | {
514 | return Data.QueryStatus != EDonNavigationQueryStatus::InProgress;
515 | }
516 |
517 | virtual void BroadcastResult() override
518 | {
519 | ResultHandler.ExecuteIfBound(Data);
520 | }
521 | };
522 |
523 | DECLARE_DYNAMIC_DELEGATE_OneParam(FDonCollisionSamplerCallback, bool, bTaskSuccessful);
524 |
525 | struct FDonMeshIdentifier
526 | {
527 | TWeakObjectPtr Mesh;
528 |
529 | FName CustomCacheIdentifier;
530 |
531 | FName UniqueTag;
532 |
533 | uint32 HashValue;
534 |
535 | friend bool operator== (const FDonMeshIdentifier& A, const FDonMeshIdentifier& B)
536 | {
537 | return A.UniqueTag.IsEqual(B.UniqueTag);
538 | }
539 |
540 | friend uint32 GetTypeHash(const FDonMeshIdentifier& Other)
541 | {
542 | return Other.HashValue;
543 | }
544 |
545 | FDonMeshIdentifier(){}
546 |
547 | explicit FDonMeshIdentifier(UPrimitiveComponent* MeshIn, FName CustomCacheIdentifierIn = FName()) : Mesh(MeshIn), CustomCacheIdentifier(CustomCacheIdentifierIn)
548 | {
549 | UniqueTag = CustomCacheIdentifier.IsNone() ? GetUniqueMeshTag(MeshIn) : CustomCacheIdentifier;
550 |
551 | if (CustomCacheIdentifier.IsNone() && MeshIn)
552 | HashValue = GetTypeHash(MeshIn);
553 | else
554 | HashValue = GetTypeHash(CustomCacheIdentifier);
555 | }
556 |
557 | private:
558 |
559 | FName GetUniqueMeshTag(UPrimitiveComponent* InMesh)
560 | {
561 | FString meshName = InMesh->GetName();
562 | FString ownerName = InMesh->GetOwner()->GetName();
563 | FString delimiter = FString("-");
564 |
565 | if (InMesh)
566 | return FName(*meshName.Append(delimiter).Append(ownerName));
567 | else
568 | return NAME_None;
569 | }
570 | };
571 |
572 | typedef TMap DonVoxelProfileCache;
573 |
574 | USTRUCT()
575 | struct FDonNavigationDynamicCollisionTask : public FDonNavigationTask
576 | {
577 | GENERATED_USTRUCT_BODY()
578 |
579 | FDonMeshIdentifier MeshId;
580 | uint32 TaskHashValue;
581 |
582 | FDonCollisionSamplerCallback ResultHandler;
583 |
584 | FDonNavigationVoxel MeshOriginalVolume;
585 |
586 | FVector MeshOriginalExtents;
587 |
588 | FString MeshAssetName;
589 |
590 | bool bReloadCollisionCache = false;
591 |
592 | bool bDisableCacheUsage = false;
593 |
594 | bool bUseCheapBoundsCollision = false;
595 |
596 | float BoundsScaleFactor = 1.f;
597 |
598 | FCollisionObjectQueryParams ObjectParams;
599 | FCollisionQueryParams CollisionParams;
600 |
601 | bool bDrawDebug = false;
602 |
603 | // Internal processing:
604 | int32 i, j, k;
605 | int xLength, yLength, zLength;
606 | bool bCollisionProfileSamplingComplete = false;
607 | bool bCollisionFetchSuccess = false;
608 | bool bCollisionOccupancyUpdatesComplete = false;
609 |
610 | float TimeTaken = 0.f;
611 |
612 | // Results:
613 | FDonVoxelCollisionProfile CollisionData;
614 |
615 | void FetchSuccess()
616 | {
617 | bCollisionProfileSamplingComplete = true;
618 | bCollisionFetchSuccess = true;
619 | }
620 |
621 | void FetchFailure()
622 | {
623 | bCollisionProfileSamplingComplete = true;
624 | bCollisionFetchSuccess = false;
625 | }
626 |
627 | virtual void BroadcastResult() override
628 | {
629 | ResultHandler.ExecuteIfBound(bCollisionFetchSuccess);
630 | }
631 |
632 | FDonNavigationDynamicCollisionTask(){}
633 | virtual ~FDonNavigationDynamicCollisionTask() {}
634 |
635 | FDonNavigationDynamicCollisionTask(FDonMeshIdentifier MeshIdIn, FDonCollisionSamplerCallback ResultHandlerIn, FDonNavigationVoxel MeshOriginalVolumeIn, bool bDisableCacheUsageIn, bool bReloadCollisionCacheIn, bool bUseCheapBoundsCollisionIn, float BoundsScaleFactorIn, bool bDrawDebugIn)
636 | : MeshId(MeshIdIn), ResultHandler(ResultHandlerIn), MeshOriginalVolume(MeshOriginalVolumeIn), bReloadCollisionCache(bReloadCollisionCacheIn), bDisableCacheUsage(bDisableCacheUsageIn), bUseCheapBoundsCollision(bUseCheapBoundsCollisionIn), BoundsScaleFactor(BoundsScaleFactorIn), bDrawDebug(bDrawDebugIn)
637 | {
638 | i = j = k = 0;
639 | TaskHashValue = GetTypeHash(MeshId.Mesh);
640 | }
641 |
642 | friend bool operator== (const FDonNavigationDynamicCollisionTask& A, const FDonNavigationDynamicCollisionTask& B)
643 | {
644 | return A.MeshId.Mesh == B.MeshId.Mesh;
645 | }
646 |
647 | friend uint32 GetTypeHash(const FDonNavigationDynamicCollisionTask& Other)
648 | {
649 | return Other.TaskHashValue; //GetTypeHash(Other.MeshId);
650 | }
651 | };
652 |
653 | struct FCollisionShape;
654 | class USceneComponent;
655 | class UBillboardComponent;
656 |
657 | /**
658 | *
659 | */
660 | UCLASS()
661 | class DONAINAVIGATION_API ADonNavigationManager : public AActor
662 | {
663 | GENERATED_BODY()
664 |
665 | public:
666 | ADonNavigationManager(const FObjectInitializer& ObjectInitializer);
667 |
668 | // Called every frame
669 | virtual void Tick(float DeltaSeconds) override;
670 | virtual void BeginPlay() override;
671 | virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
672 | virtual void OnConstruction(const FTransform& Transform) override;
673 |
674 | #if WITH_EDITOR
675 | virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
676 | #endif // WITH_EDITOR
677 |
678 | protected:
679 |
680 | FCollisionShape VoxelCollisionShape;
681 | FCollisionObjectQueryParams VoxelCollisionObjectParams;
682 | FCollisionQueryParams VoxelCollisionQueryParams;
683 | FCollisionQueryParams VoxelCollisionQueryParams2;
684 | TMap> NavGraphCache;
685 | TMap VoxelCollisionProfileCache_WorkerThread;
686 | TMap VoxelCollisionProfileCache_GameThread;
687 |
688 | bool bRegistrationCompleteForComponents;
689 | int32 RegistrationIndexCurrent;
690 | int32 MaxRegistrationsPerTick;
691 |
692 | public:
693 |
694 | void SetIsUnbound(bool bIsUnboundIn);
695 |
696 | UPROPERTY(BlueprintReadOnly, Category = "DoN Navigation")
697 | bool bIsUnbound = false;
698 |
699 | UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = Translation)
700 | USceneComponent* SceneComponent;
701 |
702 | UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = Translation)
703 | UBillboardComponent* Billboard;
704 |
705 | FDonNavVoxelXYZ NAVVolumeData;
706 |
707 | /* Represents the side of the cube used to build the voxel. Eg: a value of 300 produces a cube 300x300x300*/
708 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "World Dimensions")
709 | float VoxelSize;
710 |
711 | /* The number of voxels to build along the X axis (offset from NAV actor)*/
712 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "World Dimensions")
713 | int32 XGridSize;
714 |
715 | /* The number of voxels to build along the Y axis (offset from NAV actor)*/
716 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "World Dimensions")
717 | int32 YGridSize;
718 |
719 | /* The number of voxels to build along the Z axis (offset from NAV actor)*/
720 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "World Dimensions")
721 | int32 ZGridSize;
722 |
723 | // Collision
724 |
725 | /* Any channels added here will be treated as obstacles by the path finder*/
726 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = ObstacleDetection)
727 | TArray> ObstacleQueryChannels;
728 |
729 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = ObstacleDetection)
730 | TArray ActorsToIgnoreForCollision;
731 |
732 | /*
733 | * Some pathfinding scenarios need a special auto-correction to be applied to either origin or
734 | * destination for pathfinding to work. Eg: If a player is hiding flush with a wall then the pathfinding origin
735 | * must be offset slightly adjacent to the wall.
736 | *
737 | * Different games/maps will need different auto-correction values, some maps may need large correction values
738 | * while others may need only small adjustments. Tweak this list based on your game's needs.
739 | */
740 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = ObstacleDetection)
741 | TArray AutoCorrectionGuessList;
742 |
743 | /* Estimates the maximum possible penetration that can occur based on how Unreal's Physx handles collisions.
744 | * The default is a surprisingly high value, but this was derived from actual tests with the sample project's "thin glass tubes" usecase*/
745 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = ObstacleDetection)
746 | float UnrealPhyxPenetrationDepth = 100.f;
747 |
748 | //
749 |
750 | /** If set to true, collision checks will be performed for each and every voxel when the game begins. Warning: This can slow down loading of the game significantly.
751 | * Default behavior is set to false, meaning collision data will always be lazy loaded upn demand. This is the recommended approach */
752 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Game Startup")
753 | bool PerformCollisionChecksOnStartup;
754 |
755 | // Performance settings - Bound worlds (if multi-threading is enabled, these will be overwritten at BeginPlay with the values in the next section!)
756 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Performance Settings")
757 | bool bMultiThreadingEnabled = true;
758 |
759 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Performance Settings | Bound Worlds | SingleThread")
760 | int32 MaxPathSolverIterationsPerTick = 500;
761 |
762 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Performance Settings | Bound Worlds | SingleThread")
763 | int32 MaxCollisionSolverIterationsPerTick = 250;
764 |
765 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Performance Settings | Bound Worlds | Multithreaded")
766 | int32 MaxPathSolverIterationsOnThread = 1000;
767 |
768 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Performance Settings | Bound Worlds | Multithreaded")
769 | int32 MaxCollisionSolverIterationsOnThread = 500;
770 |
771 | // Performance settings - Infinite worlds
772 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Performance Settings | Infinite Worlds | SingleThread")
773 | int32 MaxPathSolverIterationsPerTick_Unbound = 15;
774 |
775 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Performance Settings | Infinite Worlds | SingleThread")
776 | int32 MaxCollisionSolverIterationsPerTick_Unbound = 50;
777 |
778 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Performance Settings | Infinite Worlds | Multithreaded")
779 | int32 MaxPathSolverIterationsOnThread_Unbound = 1000;
780 |
781 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Performance Settings | Infinite Worlds | Multithreaded")
782 | int32 MaxCollisionSolverIterationsOnThread_Unbound = 500;
783 |
784 | void RefreshPerformanceSettings();
785 |
786 | // World generation
787 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
788 | void ConstructBuilder();
789 |
790 | // Debug Visualization:
791 | UPROPERTY()
792 | UBoxComponent* WorldBoundaryVisualizer;
793 |
794 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Debug")
795 | bool bDisplayWorldBoundary = true;
796 |
797 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Debug")
798 | bool bDisplayWorldBoundaryInGame = false;
799 |
800 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Debug")
801 | float DebugVoxelsLineThickness = 2.f;
802 |
803 | /* This property will help you identify issues with your dynamic collision setup by performinge extra vaildations at run-time.
804 | This can be expensive so it is disabled by default. Enable if, for example, your pawns are reacting to dynamic collisions that they shouldn't actually be interested in.*/
805 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Debug")
806 | bool bRunDebugValidationsForDynamicCollisions = false;
807 |
808 | //
809 |
810 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
811 | void Debug_ToggleWorldBoundaryInGame();
812 |
813 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
814 | void Debug_DrawAllVolumes(float LineThickness);
815 |
816 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
817 | void Debug_DrawVolumesAroundPoint(FVector Location, int32 CubeSize, bool DrawPersistentLines, float Duration, float LineThickness, bool bAutoInitializeVolumes = false);
818 |
819 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
820 | void Debug_DrawVoxelCollisionProfile(UPrimitiveComponent* MeshOrPrimitive, bool bDrawPersistent = false, float Duration = 2.f);
821 |
822 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
823 | void Debug_ClearAllVolumes();
824 |
825 | void Debug_RecalculateWorldBounds()
826 | {
827 | WorldBoundaryVisualizer->SetRelativeLocation(WorldBoundsExtent());
828 | WorldBoundaryVisualizer->SetBoxExtent(WorldBoundsExtent());
829 | }
830 |
831 | private:
832 |
833 | // Graph generation
834 | void GenerateNavigationVolumePixels();
835 | void BuildNAVNetwork();
836 | void DiscoverNeighborsForVolume(int32 x, int32 y, int32 z, TArray& neighbors);
837 | void AppendImplictDOFNeighborsForVolume(int32 x, int32 y, int32 z, TArray& Neighbors);
838 | TArray FindOrSetupNeighborsForVolume(FDonNavigationVoxel* Volume);
839 |
840 | protected:
841 |
842 | float VoxelSizeSquared;
843 |
844 | // DOF based travel
845 | static const int32 Volume6DOF = 6; // 6 degrees of freedm: Forward, Backward, Left, Right, Up and Down
846 | static const int32 VolumeImplicitDOF = 12; // Implicit degrees of freedom: formed by the combination of any 2 direct degrees of freedom proving implicit access to a diagonal neighboring voxel
847 |
848 | // 6 DOF - directly usable for travel
849 | // 0 1 2 3 4 5
850 | int x6DOFCoords[Volume6DOF] = { 0, 0, 1, -1, 0, 0 };
851 | int y6DOFCoords[Volume6DOF] = { 1, -1, 0, 0, 0, 0, };
852 | int z6DOFCoords[Volume6DOF] = { 0, 0, 0, 0, 1, -1, };
853 |
854 | // Note:- 26 DOF cannot be directly used for travel as access to (x, y, z) does not guarantee access to (x + 1, y, z + 1) (etc)
855 | // unless both (x + 1, y, z) and (x, y, z + 1) are also accessible). In the latter case, such DOFs are referred to as implicit DOFs. 12 such implict DOFs are currently used.
856 |
857 | // 26 DOF table (for reference only)
858 | // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
859 | // { x + 1, x, x - 1, x + 1, x, x - 1, x + 1, x, x - 1, x + 1, x, x - 1, x + 1, /* x,*/ x - 1, x + 1, x, x - 1, x + 1, x, x - 1, x + 1, x, x - 1, x + 1, x, x - 1 };
860 | // { y - 1, y - 1, y - 1, y, y, y, y + 1, y + 1, y + 1, y - 1, y - 1, y - 1, y, /* y,*/ y, y + 1, y + 1, y + 1, y - 1, y - 1, y - 1, y, y, y, y + 1, y + 1, y + 1 };
861 | // { z + 1, z + 1, z + 1, z + 1, z + 1, z + 1, z + 1, z + 1, z + 1, z, z, z, z, /* z,*/ z, z, z, z, z - 1, z - 1, z - 1, z - 1, z - 1, z - 1, z - 1, z - 1, z - 1, };
862 |
863 |
864 | private:
865 |
866 | // Multi-threading
867 | friend class FDonNavigationWorker;
868 | class FDonNavigationWorker* WorkerThread;
869 |
870 | // Scheduled Tasks:
871 |
872 | //(owned by worker thread)
873 | TArray> ActiveNavigationTasks;
874 | TArray> ActiveDynamicCollisionTasks;
875 |
876 | //(owned by game thread)
877 | UPROPERTY()
878 | TSet ActiveNavigationTaskOwners;
879 |
880 | UPROPERTY()
881 | TSet ActiveCollisionTaskOwners;
882 |
883 | private:
884 | // @todo: Need a unified queue for new nav tasks and new nav aborts, That will ensure sequence integrity for complex usecases (pursuit/etc) that are spamming navtask/abort/navtask/abort/etc
885 | // Shared between worker thread and game thread via TQueue:
886 | //TQueue NewNavigationTasks;
887 | //TQueue NewNavigationAborts;
888 | TQueue NewNavigationTasks;
889 | TQueue NewDynamicCollisionTasks;
890 |
891 | TQueue CompletedNavigationTasks;
892 | TQueue CompletedCollisionTasks;
893 | TQueue DynamicCollisionBroadcastQueue;
894 |
895 | void ReceiveAsyncNavigationTasks();
896 | //void ReceiveAsyncAbortRequests(); // deprecated
897 | void ReceiveAsyncCollisionTasks();
898 | void ReceiveAsyncResults();
899 | void ReceiveAsyncDynamicCollisionUpdates();
900 | void DrawAsyncDebugRequests();
901 |
902 | // Multi-threading - draw debug
903 | TQueue DrawDebugLinesQueue;
904 | TQueue DrawDebugPointsQueue;
905 | TQueue DrawDebugVoxelsQueue;
906 | TQueue DrawDebugSpheresQueue;
907 |
908 | void DrawDebugLine_Safe(UWorld* World, FVector LineStart, FVector LineEnd, FColor Color, bool bPersistentLines, float LifeTime, uint8 DepthPriority, float Thickness);
909 | void DrawDebugPoint_Safe(UWorld* World, FVector PointLocation, float PointThickness, FColor Color, bool bPersistentLines, float LifeTime);
910 | void DrawDebugSphere_Safe(UWorld* World, FVector Center, float Radius, float Segments, FColor Color, bool bPersistentLines, float LifeTime);
911 | void DrawDebugVoxel_Safe(UWorld* World, FVector Center, FVector Box, FColor Color, bool bPersistentLines, float LifeTime, uint8 DepthPriority, float Thickness);
912 |
913 | // Logging:
914 | void InvalidVolumeErrorLog(FDonNavigationVoxel* OriginVolume, FDonNavigationVoxel* DestinationDestination, FVector Origin, FVector Destination);
915 | TSet UnresolvableVectors;
916 |
917 |
918 | public:
919 |
920 | // Utility functions
921 |
922 | FORCEINLINE FVector NavVolumeExtent() { return FVector(VoxelSize / 2, VoxelSize / 2, VoxelSize / 2); }
923 |
924 | FORCEINLINE FVector WorldBoundsExtent() { return FVector(XGridSize * VoxelSize / 2, YGridSize * VoxelSize / 2, ZGridSize * VoxelSize / 2); }
925 |
926 | FORCEINLINE FString VoxelUniqueKey(int x, int y, int z) { return FString::Printf(TEXT("%d-%d-%d"), x, y, z); }
927 |
928 | FORCEINLINE bool IsValidVolume(int x, int y, int z)
929 | {
930 | return NAVVolumeData.X.IsValidIndex(x) && NAVVolumeData.X[x].Y.IsValidIndex(y) && NAVVolumeData.X[x].Y[y].Z.IsValidIndex(z);
931 | }
932 |
933 | inline FVector LocationAtId(int32 X, int32 Y, int32 Z)
934 | {
935 | return GetActorLocation() + VoxelSize * FVector(X, Y, Z) + FVector(VoxelSize / 2, VoxelSize / 2, VoxelSize / 2);
936 | }
937 |
938 | inline FVector LocationAtId(FVector Location, int32 X, int32 Y, int32 Z)
939 | {
940 | return Location + VoxelSize * FVector(X, Y, Z);
941 | }
942 |
943 | inline FVector VolumeIdAt(FVector WorldLocation)
944 | {
945 | int32 x = ((WorldLocation.X - GetActorLocation().X) / VoxelSize) + (WorldLocation.X < GetActorLocation().X ? -1 : 0);
946 | int32 y = ((WorldLocation.Y - GetActorLocation().Y) / VoxelSize) + (WorldLocation.Y < GetActorLocation().Y ? -1 : 0);
947 | int32 z = ((WorldLocation.Z - GetActorLocation().Z) / VoxelSize) + (WorldLocation.Z < GetActorLocation().Z ? -1 : 0);
948 |
949 | return FVector(x, y, z);
950 | }
951 |
952 | inline FVector VolumeOriginAt(FVector WorldLocation)
953 | {
954 | FVector volumeId = VolumeIdAt(WorldLocation);
955 |
956 | return LocationAtId(volumeId.X, volumeId.Y, volumeId.Z);
957 |
958 | }
959 |
960 | inline FDonNavigationVoxel* VolumeAt(FVector WorldLocation)
961 | {
962 | int32 x = (WorldLocation.X - GetActorLocation().X) / VoxelSize;
963 | int32 y = (WorldLocation.Y - GetActorLocation().Y) / VoxelSize;
964 | int32 z = (WorldLocation.Z - GetActorLocation().Z) / VoxelSize;
965 |
966 | if (IsValidVolume(x, y, z))
967 | return &NAVVolumeData.X[x].Y[y].Z[z];
968 | else
969 | return NULL;
970 | }
971 |
972 | inline FDonNavigationVoxel* VolumeAtSafe(int32 x, int32 y, int32 z)
973 | {
974 | if (IsValidVolume(x, y, z))
975 | return &NAVVolumeData.X[x].Y[y].Z[z];
976 | else
977 | return NULL;
978 | }
979 |
980 | /*
981 | * Fetch volume by index. Unsafe, because it assumes that you've already checked index validity. It is faster, but will crash with invalid input
982 | * i.e. similar to Unreal's GetUnsafeNormal() function
983 | */
984 | inline FDonNavigationVoxel& VolumeAtUnsafe(int32 x, int32 y, int32 z)
985 | {
986 | return NAVVolumeData.X[x].Y[y].Z[z];
987 | }
988 |
989 | inline FDonNavigationVoxel* NeighborAt(FDonNavigationVoxel* Volume, FVector NeighborOffset)
990 | {
991 | if (!Volume)
992 | return NULL;
993 |
994 | int32 x = Volume->X + NeighborOffset.X;
995 | int32 y = Volume->Y + NeighborOffset.Y;
996 | int32 z = Volume->Z + NeighborOffset.Z;
997 |
998 | return VolumeAtSafe(x, y, z);
999 | }
1000 |
1001 | /* Clamps a vector to the navigation bounds as defined by the grid configuration of the navigation object you've placed in the map*/
1002 | UFUNCTION(BlueprintPure, Category = "DoN Navigation")
1003 | FVector ClampLocationToNavigableWorld(FVector DesiredLocation)
1004 | {
1005 | if (bIsUnbound)
1006 | return DesiredLocation;
1007 |
1008 | FVector origin = GetActorLocation();
1009 | float xClamped = FMath::Clamp(DesiredLocation.X, origin.X, origin.X + XGridSize * VoxelSize);
1010 | float yClamped = FMath::Clamp(DesiredLocation.Y, origin.Y, origin.Y + YGridSize * VoxelSize);
1011 | float zClamped = FMath::Clamp(DesiredLocation.Z, origin.Z, origin.Z + ZGridSize * VoxelSize);
1012 |
1013 | return FVector(xClamped, yClamped, zClamped);
1014 | }
1015 |
1016 | UFUNCTION(BlueprintPure, Category = "DoN Navigation")
1017 | bool IsLocationWithinNavigableWorld(FVector DesiredLocation) const
1018 | {
1019 | if (bIsUnbound)
1020 | return true;
1021 |
1022 | const FVector origin = GetActorLocation();
1023 |
1024 | return DesiredLocation.X >= origin.X && DesiredLocation.X <= (origin.X + XGridSize * VoxelSize) &&
1025 | DesiredLocation.Y >= origin.Y && DesiredLocation.Y <= (origin.Y + YGridSize * VoxelSize) &&
1026 | DesiredLocation.Z >= origin.Z && DesiredLocation.Z <= (origin.Z + ZGridSize * VoxelSize);
1027 | }
1028 |
1029 | ////////////////////////////////////////////////////////////////////////////////////////
1030 |
1031 | // Core API and public utility functions
1032 |
1033 | public:
1034 |
1035 | // Pathfinder:
1036 |
1037 | /**
1038 | * Schedule Pathfinding Task
1039 | *
1040 | * Schedules a pathfinding task to generate the shortest available path between a given actor and a desired destination.
1041 | * A pathfinding task can run across multiple ticks and when the results are ready the caller is immediately notified through a result handler (FDoNNavigationResultHandler)
1042 | * The caller must bind the result handler in advance to be notified when results are ready.
1043 | * If you're interested in listening to dynamic collisions that may have invalidated the path (for course correction, etc) then you must also bind the dynamic listener FDonNavigationDynamicCollisionDelegate
1044 | *
1045 | * @param Actor Actor which needs to navigate from one point to another. Typically (but not necessarily) a pawn. The location of this actor is treated as origin for pathfinding
1046 | * @param Destination Point in the world to which the actor needs to travel
1047 | * @param QueryParams Additional params for customizing the path finding query
1048 | * @param DebugParams Use these debug params to enable visualization of the raw and optimized paths and other debug related activities
1049 | * @param ResultHandlerDelegate You must bind a function of your choice to this delegate to be notified when pathfinding results are available for you to use
1050 | * @param DynamicCollisionListener This listener allows you to be notified whenever your path solution has been invalidated by dynamic obstacles that have occupied parts of your path solution
1051 | * that may previously have been navigable. Typically this means you should immediately reschedule your query to obtain a revised path solution
1052 | */
1053 |
1054 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
1055 | bool SchedulePathfindingTask(AActor* Actor, FVector Destination, UPARAM(ref) const FDoNNavigationQueryParams& QueryParams, UPARAM(ref) const FDoNNavigationDebugParams& DebugParams, FDoNNavigationResultHandler ResultHandlerDelegate, FDonNavigationDynamicCollisionDelegate DynamicCollisionListener);
1056 |
1057 | /** Aborts an existing pathfinding task for a given Actor */
1058 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
1059 | void AbortPathfindingTask(AActor* Actor);
1060 |
1061 | /** Does this actor have an active pathfinding task already scheduled with the navigation manager? */
1062 | UFUNCTION(BlueprintPure, Category = "DoN Navigation")
1063 | bool HasTask(AActor* Actor) { return ActiveNavigationTaskOwners.Contains(Actor); }
1064 |
1065 |
1066 | /**
1067 | * Schedule Dynamic Collision Update For Mesh
1068 | *
1069 | * Updates the collision status of all voxels within a mesh's visibility bounds. You should call this function whenever a dynamic obstacle in your scene
1070 | * moves, scales or morphs in any way that affects the collision. Always remember that the DoN Navigation system does not automatically detect dynamic voxel collision
1071 | * as that would be too expensive for the system to manage (potentially millions of voxels reside in a scene). Therefore it relies on users of the plugin to manually
1072 | * trigger dynamic collision depending on the unique needs of a particular project. You only need this for any object that moves after the game has begun, voxel collision
1073 | * for static objects is exempt from this as they're managed through a different code path and lazy-loaded on demand.
1074 | *
1075 | * This function is expensive as it samples per-voxel collision so use it with care.
1076 | *
1077 | * @param Mesh The mesh (usually a movable object) whose collision influence needs to be updated around the current location of the mesh
1078 | *
1079 | * @param ResultHandler Use this delegate to be notified when the task is complete and to learn its final status
1080 | *
1081 | * @param CustomCacheIdentifier Use this to share a single collision profile across multiple meshes or to fully customize what gets loaded into the collision cache.
1082 | *
1083 | * Details: By default the collision cache uses the address of each mesh component as the cache key. This has two fundamental limitations:
1084 | * 1. Multiple meshes with identical collision properties will end up creating individual entries in the cache despite being the same (collision wise).
1085 | * 2. Any mesh that needs to change its rotation or scale cannot use the default cache value which only works for location based translations.
1086 | *
1087 | * The first limitation can be easily solved by sharing a single cache identifier across all meshes of the same type (Eg: "SolidWall_NoRotation").
1088 | * The second limitation can be resolved either by forcibly reloading the collision cache each time using bReloadCollisionCache (not recommended, very expensive)
1089 | * OR you can use the Custom Cache Identifier to define cardinal translation points for your mesh as follows:
1090 | * Eg: Mesh Scale = 1.5f -> CustomCacheIdentifier = "SolidWall_NoRotation_150p",
1091 | * Eg: Mesh Scale = 2.0f -> CustomCacheIdentifier = "SolidWall_NoRotation_200p", etc.
1092 | * Using this technique instead of forcibly reloading the cache will improve your performance. Try to keep your identifiers short though!
1093 | *
1094 | * @param bReplaceExistingTask By default the scheduler will ignore new tasks for a mesh if it already has one running. Use this to forcibly replace an existing task.
1095 | * This is useful for advanced usecases where you're relying on the sequence of dynamic collision updates or triggering other events dependent on its success
1096 | *
1097 | * @param bReloadCollisionCache Default is false. The system maintains a cache of voxel collision profiles associated with a mesh. A cache entry is created for a mesh
1098 | * the first time you call this function for it. If you really want to overwrite the cache value associated with a mesh
1099 | * then set bReloadCollisionCache to true. However for most usecases it is recommended to use CustomCacheIdentifier instead.
1100 | *
1101 | *
1102 | * @param bUseCheapBoundsCollision Marks all voxels within the mesh's visibility bounds as collided. This is several times faster that per-voxel sampling. Great for simple meshes like walls
1103 | * whose visibility bounds closely aligns with the actual mesh itself.
1104 | *
1105 | * @param BoundsScaleFactor Use this multiplier to increase or decrease the size of the bounding box derived from your mesh within which voxel collisions are sampled.
1106 | *
1107 | */
1108 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
1109 | bool ScheduleDynamicCollisionUpdate(UPrimitiveComponent* Mesh, FDonCollisionSamplerCallback ResultHandler, FName CustomCacheIdentifier = NAME_None, bool bReplaceExistingTask = false, bool bDisableCacheUsage = false, bool bReloadCollisionCache = false, bool bUseCheapBoundsCollision = false, float BoundsScaleFactor = 1.f, bool bForceSynchronousExecution = false, bool bDrawDebug = false);
1110 |
1111 | /**
1112 | * Unregisters a given dynamic collision listener from a given volume. Your should always call this function whenever a particular actor or object is
1113 | * no longer interested in listening to collisions in a particular area. This is especially important for maintaining performance as
1114 | * accumulating unwanted collision listeners will clog up the system quickly and affect performance.
1115 | */
1116 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
1117 | void StopListeningToDynamicCollisionsForPath(FDonNavigationDynamicCollisionDelegate ListenerToClear, UPARAM(ref) const FDoNNavigationQueryData& QueryData);
1118 |
1119 | /**
1120 | * Similar to StopListeningToDynamicCollisionsForPath, but operates on a single index. If you're using the pathfinding API directly, use this to "clean up" behind your pawn as it passes dynamic collision geometry
1121 | * (For users using the "Fly To" behavior tree node you don't need to worry about this as all cleanup is taken care of for you)
1122 | */
1123 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
1124 | void StopListeningToDynamicCollisionsForPathIndex(FDonNavigationDynamicCollisionDelegate ListenerToClear, UPARAM(ref) const FDoNNavigationQueryData& QueryData, const int32 VolumeIndex);
1125 |
1126 | void VoxelCacheClearByKey(const FDonMeshIdentifier &MeshId)
1127 | {
1128 | VoxelCollisionProfileCache_WorkerThread.Remove(MeshId);
1129 | }
1130 |
1131 | /**
1132 | * WARNING: This function is for stress-testing for performance only, it operates synchronously unlike the scheduler functions making it great for profiling sessions.
1133 | * Use SchedulePathfindingTask for regular navigation and pathfinding usecases.
1134 | *
1135 | * Given an actor (representing origin) and a destination point in the world, this function generates the shortest path between the two.
1136 | *
1137 | * @param Actor Actor which needs to navigate from one point to another. Typically (but not necessarily) a pawn. The location of this actor is treated as origin for pathfinding
1138 | * @param Destination Point in the world to which the actor needs to travel
1139 | * @param PathSolutionRaw Path solution as an array of FVectors representing the shortest path from origin to destination
1140 | * @param PathSolutionOptimized Path solution optimized from the raw path by performing collision sweeps using the actor's collision component
1141 | * @param QueryParams Additional params for the path finding query
1142 | * @param DebugParams Use these debug params to enable visualization of the raw and optimized paths and other debug related activities
1143 | */
1144 |
1145 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
1146 | bool FindPathSolution_StressTesting(AActor* Actor, FVector Destination, TArray &PathSolutionRaw, TArray &PathSolutionOptimized, UPARAM(ref) const FDoNNavigationQueryParams& QueryParams, UPARAM(ref) const FDoNNavigationDebugParams& DebugParams);
1147 |
1148 | // Tracing utility
1149 |
1150 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
1151 | bool IsDirectPathSweep(UPrimitiveComponent* CollisionComponent, FVector Start, FVector End, FHitResult &OutHit, bool bFindInitialOverlaps = false, float CollisionShapeInflation = 0.f);
1152 |
1153 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
1154 | bool IsDirectPathLineTrace(FVector start, FVector end, FHitResult &OutHit, const TArray &ActorsToIgnore, bool bFindInitialOverlaps = true);
1155 |
1156 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
1157 | bool IsDirectPathLineSweep(UPrimitiveComponent* CollisionComponent, FVector Start, FVector End, FHitResult &OutHit, bool bFindInitialOverlaps = false, float CollisionShapeInflation = 0.f);
1158 |
1159 | // Shape based:
1160 | bool IsDirectPathSweepShape(const FCollisionShape& Shape, FVector Start, FVector End, FHitResult &OutHit, bool bFindInitialOverlaps = false);
1161 | bool IsDirectPathLineSweepShape(const FCollisionShape& Shape, FVector Start, FVector End, FHitResult &OutHit, bool bFindInitialOverlaps = false);
1162 |
1163 |
1164 | // AI Utility Functions
1165 | UFUNCTION(BlueprintPure, Category = "DoN Navigation")
1166 | FVector FindRandomPointFromActorInNavWorld(AActor* Actor, float Distance, bool& bFoundValidResult, float MaxDesiredAltitude = -1.f, float MaxZAngularDispacement = 15.f, int32 MaxAttempts = 5);
1167 |
1168 | UFUNCTION(BlueprintPure, Category = "DoN Navigation")
1169 | FVector FindRandomPointAroundOriginInNavWorld(AActor* NavigationActor, FVector Origin, float Distance, bool& bFoundValidResult, float MaxDesiredAltitude = -1.f, float MaxZAngularDispacement = 15.f, int32 MaxAttempts = 5);
1170 |
1171 | /* This is an edge case where the goal is beneath the landscape (and therefore can never be reached). This situation should be identified preemptively and dealt with to prevent a futile and expensive call*/
1172 | UFUNCTION(BlueprintPure, Category = "DoN Navigation")
1173 | bool IsLocationBeneathLandscape(FVector Location, float LineTraceHeight = 3000.f);
1174 |
1175 | UFUNCTION(BlueprintPure, Category = "DoN Navigation")
1176 | bool IsMeshBoundsWithinNavigableWorld(UPrimitiveComponent* Mesh, float BoundsScaleFactor = 1.f);
1177 |
1178 | // Debug helpers:
1179 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
1180 | void VisualizeNAVResult(UPARAM(ref) const TArray& PathSolution, FVector Source, FVector Destination, bool Reset, UPARAM(ref) const FDoNNavigationDebugParams& DebugParams, UPARAM(ref) FColor const& LineColor);
1181 |
1182 | UFUNCTION(BlueprintCallable, Category = "DoN Navigation")
1183 | void VisualizeDynamicCollisionListeners(FDonNavigationDynamicCollisionDelegate Listener, UPARAM(ref) const FDoNNavigationQueryData& QueryData);
1184 |
1185 | // NAV Visualizer
1186 | void VisualizeSolution(FVector source, FVector destination, const TArray& PathSolutionRaw, const TArray& PathSolutionOptimized, const FDoNNavigationDebugParams& DebugParams);
1187 |
1188 | // Logging utility
1189 | static FString GetMeshLogIdentifier(UPrimitiveComponent* Mesh);
1190 |
1191 | ////////////////////////////////////////////////////////////////////////////////////////
1192 |
1193 | private:
1194 |
1195 | // Core pathfinding algorithms
1196 | void TickScheduledPathfindingTasks(float DeltaSeconds, int32 MaxIterationsPerTick);
1197 | void TickScheduledPathfindingTasks_Safe(float DeltaSeconds, int32 MaxIterationsPerTick);
1198 | void TickScheduledCollisionTasks(float DeltaSeconds, int32 MaxIterationsPerTick);
1199 | void TickScheduledCollisionTasks_Safe(float DeltaSeconds, int32 MaxIterationsPerTick);
1200 |
1201 | protected:
1202 | // These virtual functions are overridden for the Finite and Infinite implementations of the plugin (see DonNavigationManager.cpp and DonNavigationManagerUnbound.cpp)
1203 | virtual void TickNavigationSolver(FDonNavigationQueryTask& task);
1204 | virtual bool PrepareSolution(FDonNavigationQueryTask& Task);
1205 |
1206 | private:
1207 | void TickNavigationOptimizer(FDonNavigationQueryTask& task);
1208 | void TickNavigationOptimizerCycle(FDonNavigationQueryTask& task, int32& IterationsProcessed, const int32 MaxIterationsPerTask);
1209 | void TickVoxelCollisionSampler(FDonNavigationDynamicCollisionTask& Task);
1210 | void ExpandFrontierTowardsTarget(FDonNavigationQueryTask& Task, FDonNavigationVoxel* Current, FDonNavigationVoxel* Neighbor);
1211 | void PackageRawSolution(FDonNavigationQueryTask& task);
1212 | void PackageDirectSolution(FDonNavigationQueryTask& Task);
1213 |
1214 | // Thread-aware routines
1215 | //FCriticalSection CriticalSection_Collisions;
1216 |
1217 | void AddPathfindingTask(const FDonNavigationQueryTask& Task);
1218 | void AddDynamicCollisionTask(FDonNavigationDynamicCollisionTask& Task);
1219 | bool IsDynamicCollisionTaskActive(const FDonNavigationDynamicCollisionTask& Task);
1220 | bool PrepareDynamicCollisionTask(FDonNavigationDynamicCollisionTask& task, bool &bOverallStatus);
1221 | void CompleteNavigationTask(int32 TaskIndex);
1222 | void CompleteCollisionTask(const int32 TaskIndex, bool bIsSuccess);
1223 |
1224 | void AbortPathfindingTask_Internal(AActor* Actor);
1225 | void AbortPathfindingTaskByIndex(int32 TaskIndex);
1226 | inline void CleanupExistingTaskForActor(AActor* Actor) { AbortPathfindingTask(Actor); }
1227 |
1228 | // Voxel collision sampling:
1229 | void UpdateVoxelCollision(FDonNavigationVoxel& Volume);
1230 | FDonVoxelCollisionProfile GetVoxelCollisionProfileFromMesh(const FDonMeshIdentifier& MeshId, bool &bResultIsValid, DonVoxelProfileCache& PreferredCache, bool bIgnoreMeshOriginOccupancy = false, bool bDisableCacheUsage = false, FName CustomCacheIdentifier = NAME_None, bool bReloadCollisionCache = false, bool bUseCheapBoundsCollision = false, float BoundsScaleFactor = 1.f, bool DrawDebug = false);
1231 | FDonVoxelCollisionProfile SampleVoxelCollisionForMesh(UPrimitiveComponent* Mesh, bool &bResultIsValid, bool bIgnoreMeshOriginOccupancy = false, FName CustomCacheIdentifier = NAME_None, bool bUseCheapBoundsCollision = false, float BoundsScaleFactor = 1.f, bool DrawDebug = false);
1232 |
1233 | // Dynamic collision listeners:
1234 | void DynamicCollisionUpdateForMesh(const FDonMeshIdentifier& MeshId, FDonVoxelCollisionProfile& VoxelCollisionProfile, bool bDisableCacheUsage = false, bool bDrawDebug = false);
1235 | void AddCollisionListenerToVolumeFromTask(FDonNavigationVoxel* Volume, FDonNavigationQueryTask& task);
1236 | FDonNavigationVoxel* AppendVolumeList(FVector Location, FDonNavigationQueryTask& task);
1237 | void AppendVolumeListFromRange(FVector Start, FVector End, FDonNavigationQueryTask& task);
1238 |
1239 | // Finite World: (think in terms of Volumes)
1240 | FDonNavigationVoxel* ResolveVolume(FVector &DesiredLocation, UPrimitiveComponent* CollisionComponent, bool bFlexibleOriginGoal = true, float CollisionShapeInflation = 0.f, bool bShouldSweep = true);
1241 | FDonNavigationVoxel* GetClosestNavigableVolume(FVector DesiredLocation, UPrimitiveComponent* CollisionComponent, bool &bInitialPositionCollides, float CollisionShapeInflation = 0.f, bool bShouldSweep = true);
1242 | FDonNavigationVoxel* GetBestNeighborRecursive(FDonNavigationVoxel* Volume, int32 CurrentDepth, int32 NeighborSearchMaxDepth, FVector Location, UPrimitiveComponent* CollisionComponent, bool bConsiderInitialOverlaps, float CollisionShapeInflation, bool bShouldSweep);
1243 |
1244 | // Infinite World: (think in terms of Vectors)
1245 | bool ResolveVector(FVector &DesiredLocation, FVector &ResolvedLocation, UPrimitiveComponent* CollisionComponent, bool bFlexibleOriginGoal = true, float CollisionShapeInflation = 0.f, bool bShouldSweep = true);
1246 | bool GetClosestNavigableVector(FVector DesiredLocation, FVector &ResolvedLocation, UPrimitiveComponent* CollisionComponent, bool &bInitialPositionCollides, float CollisionShapeInflation = 0.f, bool bShouldSweep = true);
1247 |
1248 | public:
1249 | UFUNCTION(BlueprintCallable, BlueprintPure, Category = "DoN Navigation")
1250 | bool CanNavigate(FVector Location);
1251 |
1252 | bool CanNavigate(FDonNavigationVoxel* Volume);
1253 |
1254 | protected:
1255 | bool CanNavigateByCollisionProfile(FDonNavigationVoxel* Volume, const FDonVoxelCollisionProfile& CollisionToTest);
1256 | bool CanNavigateByCollisionProfile(FVector Location, const FDonVoxelCollisionProfile& CollisionToTest);
1257 |
1258 | private:
1259 |
1260 | // Path solution generation and optimization pass
1261 | void PathSolutionFromVolumeSolution(const TArray& VolumeSolution, TArray &PathSolution, FVector Origin, FVector Destination, const FDoNNavigationDebugParams& DebugParams);
1262 | void OptimizePathSolution(UPrimitiveComponent* CollisionComponent, const TArray& PathSolution, TArray &PathSolutionOptimized, float CollisionShapeInflation = 0.f);
1263 | void OptimizePathSolution_Pass1_LineTrace(UPrimitiveComponent* CollisionComponent, const TArray& PathSolution, TArray &PathSolutionOptimized, float CollisionShapeInflation = 0.f);
1264 |
1265 | ////////////////////////////////////////////////////////////////////////////////////////
1266 | };
--------------------------------------------------------------------------------
/Source/DonAINavigation/Classes/DonNavigationManagerUnbound.h:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
12 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13 |
14 | #pragma once
15 |
16 | #include "DonNavigationManager.h"
17 |
18 | #include "DonNavigationManagerUnbound.generated.h"
19 |
20 | /*
21 | * Infinite Worlds! This is the unbound version of the Navigation Manager.
22 | * Supports unlimited map sizes. Nothing is cached, everything is looked up on-demand and for procedural games it fully eliminates the burden of having to manage dynamic collision updates.
23 | * It is obviously slower than the Finite World equivalent but will benefit projects with huge maps or highly dynamic/frequently changing/procedural collision geometry.
24 | */
25 | UCLASS()
26 | class DONAINAVIGATION_API ADonNavigationManagerUnbound : public ADonNavigationManager
27 | {
28 | GENERATED_BODY()
29 |
30 | public:
31 | ADonNavigationManagerUnbound(const FObjectInitializer& ObjectInitializer);
32 |
33 | virtual void BeginPlay() override;
34 |
35 | protected:
36 | virtual void TickNavigationSolver(FDonNavigationQueryTask& task) override;
37 | virtual bool PrepareSolution(FDonNavigationQueryTask& Task) override;
38 |
39 | TArray NeighborsAsVectors(FVector Location);
40 | void ExpandFrontierTowardsTarget(FDonNavigationQueryTask& Task, FVector Current, FVector Neighbor);
41 | };
42 |
--------------------------------------------------------------------------------
/Source/DonAINavigation/Classes/DonNavigatorInterface.h:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | #pragma once
16 |
17 | #include "DonNavigatorInterface.generated.h"
18 |
19 | UINTERFACE()
20 | class DONAINAVIGATION_API UDonNavigator : public UInterface
21 | {
22 | GENERATED_UINTERFACE_BODY()
23 | };
24 |
25 | struct FDonVoxelCollisionProfile;
26 |
27 | class DONAINAVIGATION_API IDonNavigator
28 | {
29 | GENERATED_IINTERFACE_BODY()
30 |
31 | // TBD - earlier this was meant to enforce that users of the API would provide minimum metadata for navigators which the system would then use in its calculations,
32 | // but this has since been sidelined to make it easier for new consumers of the system to rapidly get going.
33 |
34 | /* Optional custom movement input. If not provided, default AddMovementInput behavior will be called on the pawn or character*/
35 | UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Don Navigation")
36 | void AddMovementInputCustom(FVector WorldDirection, float ScaleValue);
37 |
38 | UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Don Navigation")
39 | void OnLocomotionBegin();
40 |
41 | UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Don Navigation")
42 | void OnLocomotionEnd(const bool bLocomotionSuccess);
43 |
44 | UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Don Navigation")
45 | void OnLocomotionAbort();
46 |
47 | UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Don Navigation")
48 | void OnNextSegment(FVector NextPoint);
49 | };
--------------------------------------------------------------------------------
/Source/DonAINavigation/Classes/Multithreading/DonDrawDebugThreadSafe.h:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | #pragma once
16 |
17 | struct FDrawDebugLineRequest
18 | {
19 | FVector LineStart;
20 | FVector LineEnd;
21 | FColor Color;
22 | bool bPersistentLines;
23 | float LifeTime;
24 | uint8 DepthPriority;
25 | float Thickness;
26 |
27 | FDrawDebugLineRequest() {}
28 |
29 | FDrawDebugLineRequest(FVector LineStartIn, FVector LineEndIn, FColor ColorIn, bool bPersistentLinesIn, float LifeTimeIn, uint8 DepthPriorityIn, float ThicknessIn)
30 | : LineStart(LineStartIn), LineEnd(LineEndIn), Color(ColorIn), bPersistentLines(bPersistentLinesIn), LifeTime(LifeTimeIn), DepthPriority(DepthPriorityIn), Thickness(ThicknessIn)
31 | {
32 |
33 | }
34 | };
35 |
36 | struct FDrawDebugPointRequest
37 | {
38 | FVector PointLocation;
39 | float PointThickness;
40 | FColor Color;
41 | bool bPersistentLines;
42 | float LifeTime;
43 |
44 | FDrawDebugPointRequest() {}
45 |
46 | FDrawDebugPointRequest(FVector PointLocationIn, float PointThicknessIn, FColor ColorIn, bool bPersistentLinesIn, float LifeTimeIn)
47 | : PointLocation(PointLocationIn), PointThickness(PointThicknessIn), Color(ColorIn), bPersistentLines(bPersistentLinesIn), LifeTime(LifeTimeIn)
48 | {
49 |
50 | }
51 | };
52 |
53 | struct FDrawDebugVoxelRequest
54 | {
55 | FVector Center;
56 | FVector Box;
57 | FColor Color;
58 | bool bPersistentLines;
59 | float LifeTime;
60 | uint8 DepthPriority;
61 | float Thickness;
62 |
63 | FDrawDebugVoxelRequest() {}
64 |
65 | FDrawDebugVoxelRequest(FVector CenterIn, FVector BoxIn, FColor ColorIn, bool bPersistentLinesIn, float LifeTimeIn, uint8 DepthPriorityIn, float ThicknessIn)
66 | : Center(CenterIn), Box(BoxIn), Color(ColorIn), bPersistentLines(bPersistentLinesIn), LifeTime(LifeTimeIn), DepthPriority(DepthPriorityIn), Thickness(ThicknessIn)
67 | {
68 |
69 | }
70 | };
71 |
72 | struct FDrawDebugSphereRequest
73 | {
74 | FVector Center;
75 | float Radius;
76 | float Segments;
77 | FColor Color;
78 | bool bPersistentLines;
79 | float LifeTime;
80 |
81 | FDrawDebugSphereRequest() {}
82 |
83 | FDrawDebugSphereRequest(FVector CenterIn, float RadiusIn, float SegmentsIn, FColor ColorIn, bool bPersistentLinesIn, float LifeTimeIn)
84 | : Center(CenterIn), Radius(RadiusIn), Segments(SegmentsIn), Color(ColorIn), bPersistentLines(bPersistentLinesIn), LifeTime(LifeTimeIn)
85 | {
86 |
87 | }
88 | };
--------------------------------------------------------------------------------
/Source/DonAINavigation/Classes/Multithreading/DonNavigationWorker.h:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 |
16 | #pragma once
17 |
18 | class ADonNavigationManager;
19 |
20 | class FDonNavigationWorker: public FRunnable
21 | {
22 | FRunnableThread* Thread;
23 |
24 | ADonNavigationManager* Manager;
25 |
26 | FThreadSafeCounter StopTaskCounter;
27 |
28 | public:
29 | FDonNavigationWorker();
30 | FDonNavigationWorker(ADonNavigationManager* Manager, int32 MaxPathSolverIterations, int32 MaxCollisionSolverIterations);
31 | virtual ~FDonNavigationWorker();
32 |
33 | //FRunnable interface
34 | virtual bool Init();
35 | virtual uint32 Run();
36 | virtual void Stop();
37 |
38 | void ShutDown();
39 |
40 | private:
41 |
42 | // Perform work:
43 | void SolveNavigationTasks();
44 |
45 | int32 MaxPathSolverIterations;
46 | int32 MaxCollisionSolverIterations;
47 | };
48 |
--------------------------------------------------------------------------------
/Source/DonAINavigation/DonAINavigation.Build.cs:
--------------------------------------------------------------------------------
1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2 |
3 | namespace UnrealBuildTool.Rules
4 | {
5 | public class DonAINavigation : ModuleRules
6 | {
7 | public DonAINavigation(ReadOnlyTargetRules Target) : base(Target)
8 | {
9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
10 | bEnableShadowVariableWarnings = false;
11 |
12 | PublicIncludePaths.AddRange(
13 | new string[] {
14 | // ... add public include paths required here ...
15 | }
16 | );
17 |
18 | PrivateIncludePaths.AddRange(
19 | new string[] {
20 | // ... add other private include paths required here ...
21 | }
22 | );
23 |
24 | PublicDependencyModuleNames.AddRange(
25 | new string[]
26 | {
27 | "Core",
28 | "CoreUObject",
29 | // ... add other public dependencies that you statically link with here ...
30 | "AIModule",
31 | "GameplayTasks",
32 | "Engine",
33 | }
34 | );
35 |
36 | PrivateDependencyModuleNames.AddRange(
37 | new string[]
38 | {
39 | // ... add private dependencies that you statically link with here ...
40 | }
41 | );
42 |
43 | DynamicallyLoadedModuleNames.AddRange(
44 | new string[]
45 | {
46 | // ... add any modules that your module loads dynamically here ...
47 | }
48 | );
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/Source/DonAINavigation/Private/BehaviorTree/BTTask_FlyTo.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | #include "BehaviorTree/BTTask_FlyTo.h"
16 | #include "../DonAINavigationPrivatePCH.h"
17 |
18 | #include "DonNavigatorInterface.h"
19 |
20 | #include "BehaviorTree/BlackboardComponent.h"
21 | #include "BehaviorTree/Blackboard/BlackboardKeyType_Object.h"
22 | #include "BehaviorTree/Blackboard/BlackboardKeyType_Vector.h"
23 | #include "BehaviorTree/BTFunctionLibrary.h"
24 |
25 | #include "Runtime/AIModule/Classes/AIController.h"
26 | #include "VisualLogger/VisualLogger.h"
27 |
28 | UBTTask_FlyTo::UBTTask_FlyTo(const FObjectInitializer& ObjectInitializer)
29 | : Super(ObjectInitializer)
30 | , bRecalcPathOnDestinationChanged(false)
31 | , RecalculatePathTolerance(50.f)
32 | {
33 | NodeName = "Fly To";
34 | bNotifyTick = true;
35 |
36 | FlightLocationKey.AddVectorFilter(this, GET_MEMBER_NAME_CHECKED(UBTTask_FlyTo, FlightLocationKey));
37 | FlightResultKey.AddBoolFilter(this, GET_MEMBER_NAME_CHECKED(UBTTask_FlyTo, FlightResultKey));
38 | KeyToFlipFlopWhenTaskExits.AddBoolFilter(this, GET_MEMBER_NAME_CHECKED(UBTTask_FlyTo, KeyToFlipFlopWhenTaskExits));
39 |
40 | FlightLocationKey.AllowNoneAsValue(true);
41 | FlightResultKey.AllowNoneAsValue(true);
42 | KeyToFlipFlopWhenTaskExits.AllowNoneAsValue(true);
43 | }
44 |
45 | void UBTTask_FlyTo::InitializeFromAsset(UBehaviorTree& Asset)
46 | {
47 | Super::InitializeFromAsset(Asset);
48 |
49 | auto blackboard = GetBlackboardAsset();
50 | if (!blackboard)
51 | return;
52 |
53 | FlightLocationKey.ResolveSelectedKey(*blackboard);
54 | }
55 |
56 | EBTNodeResult::Type UBTTask_FlyTo::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
57 | {
58 | EBTNodeResult::Type NodeResult = SchedulePathfindingRequest(OwnerComp, NodeMemory);
59 |
60 | if (bRecalcPathOnDestinationChanged && (NodeResult == EBTNodeResult::InProgress))
61 | {
62 | UBlackboardComponent* BlackboardComp = OwnerComp.GetBlackboardComponent();
63 | auto myMemory = (FBT_FlyToTarget*)NodeMemory;
64 | if (ensure(BlackboardComp))
65 | {
66 | if (myMemory->BBObserverDelegateHandle.IsValid())
67 | {
68 | UE_VLOG(OwnerComp.GetAIOwner(), LogBehaviorTree, Warning, TEXT("UBTTask_MoveTo::ExecuteTask \'%s\' Old BBObserverDelegateHandle is still valid! Removing old Observer."), *GetNodeName());
69 | BlackboardComp->UnregisterObserver(FlightLocationKey.GetSelectedKeyID(), myMemory->BBObserverDelegateHandle);
70 | }
71 | myMemory->BBObserverDelegateHandle = BlackboardComp->RegisterObserver(FlightLocationKey.GetSelectedKeyID(), this, FOnBlackboardChangeNotification::CreateUObject(this, &UBTTask_FlyTo::OnBlackboardValueChange));
72 | }
73 | }
74 |
75 | return NodeResult;
76 | }
77 |
78 | EBTNodeResult::Type UBTTask_FlyTo::SchedulePathfindingRequest(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
79 | {
80 | auto pawn = OwnerComp.GetAIOwner()->GetPawn();
81 | auto myMemory = (FBT_FlyToTarget*)NodeMemory;
82 | auto blackboard = pawn ? pawn->GetController()->FindComponentByClass() : NULL;
83 |
84 | /*const float currentTime = GetWorld()->GetRealTimeSeconds();
85 | const float lastRequestTimestamp = LastRequestTimestamps.FindOrAdd(pawn);
86 | if (currentTime - lastRequestTimestamp < RequestThrottleInterval)
87 | return EBTNodeResult::Failed; // throttled
88 | else
89 | LastRequestTimestamps.Add(pawn, currentTime); //LastRequestTimestamp = currentTime;
90 | */
91 | NavigationManager = UDonNavigationHelper::DonNavigationManagerForActor(pawn);
92 | if (NavigationManager->HasTask(pawn) && !QueryParams.bForceRescheduleQuery)
93 | return EBTNodeResult::Failed; // early exit instead of going through the manager's internal checks and fallback via HandleTaskFailure (which isn't appropriate here)
94 |
95 | // Validate internal state:
96 | if (!pawn || !myMemory || !blackboard || !NavigationManager)
97 | {
98 | UE_LOG(DoNNavigationLog, Log, TEXT("BTTask_FlyTo has invalid data for AI Pawn or NodeMemory or NavigationManager. Unable to proceed."));
99 |
100 | return HandleTaskFailure(OwnerComp, NodeMemory, blackboard);
101 | }
102 |
103 | // Validate blackboard key data:
104 | if(FlightLocationKey.SelectedKeyType != UBlackboardKeyType_Vector::StaticClass())
105 | {
106 | UE_LOG(DoNNavigationLog, Log, TEXT("Invalid FlightLocationKey. Expected Vector type, found %s"), *(FlightLocationKey.SelectedKeyType ? FlightLocationKey.SelectedKeyType->GetName() : FString("?")));
107 | return HandleTaskFailure(OwnerComp, NodeMemory, blackboard);
108 | }
109 |
110 | // Prepare input:
111 | myMemory->Reset();
112 | myMemory->Metadata.ActiveInstanceIdx = OwnerComp.GetActiveInstanceIdx();
113 | myMemory->Metadata.OwnerComp = &OwnerComp;
114 | myMemory->QueryParams = QueryParams;
115 | myMemory->QueryParams.CustomDelegatePayload = &myMemory->Metadata;
116 | myMemory->bIsANavigator = pawn->GetClass()->ImplementsInterface(UDonNavigator::StaticClass());
117 |
118 | FVector flightDestination = blackboard->GetValueAsVector(FlightLocationKey.SelectedKeyName);
119 | myMemory->TargetLocation = flightDestination;
120 |
121 | // Bind result notification delegate:
122 | FDoNNavigationResultHandler resultHandler;
123 | resultHandler.BindDynamic(this, &UBTTask_FlyTo::Pathfinding_OnFinish);
124 |
125 | // Bind dynamic collision updates delegate:
126 | myMemory->DynamicCollisionListener.BindDynamic(this, &UBTTask_FlyTo::Pathfinding_OnDynamicCollisionAlert);
127 |
128 | // Schedule task:
129 | bool bTaskScheduled = false;
130 | bTaskScheduled = NavigationManager->SchedulePathfindingTask(pawn, flightDestination, myMemory->QueryParams, DebugParams, resultHandler, myMemory->DynamicCollisionListener);
131 |
132 | if (bTaskScheduled)
133 | {
134 | return EBTNodeResult::InProgress;
135 | }
136 | else
137 | return HandleTaskFailure(OwnerComp, NodeMemory, blackboard);
138 | }
139 |
140 | void UBTTask_FlyTo::AbortPathfindingRequest(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
141 | {
142 | APawn* pawn = OwnerComp.GetAIOwner()->GetPawn();
143 | FBT_FlyToTarget* myMemory = (FBT_FlyToTarget*)NodeMemory;
144 |
145 | if (NavigationManager && pawn && myMemory)
146 | {
147 | NavigationManager->AbortPathfindingTask(pawn);
148 |
149 | // Unregister all dynamic collision listeners. We've completed our task and are no longer interested in listening to these:
150 | NavigationManager->StopListeningToDynamicCollisionsForPath(myMemory->DynamicCollisionListener, myMemory->QueryResults);
151 | }
152 | }
153 |
154 | FBT_FlyToTarget* UBTTask_FlyTo::TaskMemoryFromGenericPayload(void* GenericPayload)
155 | {
156 | // A brief explanation of the "NodeMemory" and "Generic Payload" business:
157 |
158 | // AFAICT, Behavior tree tasks operate as singletons and internally maintain an instance memory stack which maps instance data for every AI currently running this task.
159 | // So the BT Task itself is shared by all AI pawns and does not have sufficient information to handle our result delegate on its own.
160 | // Because of this, we use a custom delegate payload (which we passed earlier in "ExecuteTask") to lookup the actual AI owner and the correct NodeMemory
161 | // inside which we store the pathfinding results.
162 |
163 | auto payload = static_cast (GenericPayload);
164 | auto ownerComp = (payload && payload->OwnerComp.IsValid()) ? payload->OwnerComp.Get() : NULL;
165 |
166 | // Is the pawn's BrainComponent still alive and valid?
167 | if (!ownerComp)
168 | return NULL;
169 |
170 | // Is it still working on this task or has it moved on to another one?
171 | if (ownerComp->GetTaskStatus(this) != EBTTaskStatus::Active)
172 | {
173 | UE_LOG(DoNNavigationLog, Warning, TEXT("Task (Fly To) is not active."));
174 | return nullptr;
175 | }
176 |
177 | // Validations passed, should be safe to work on NodeMemory now:
178 |
179 | auto nodeMemory = ownerComp->GetNodeMemory(this, ownerComp->GetActiveInstanceIdx());
180 | auto myMemory = nodeMemory ? reinterpret_cast (nodeMemory) : NULL;
181 |
182 | return myMemory;
183 | }
184 |
185 | void UBTTask_FlyTo::Pathfinding_OnFinish(const FDoNNavigationQueryData& Data)
186 | {
187 | auto myMemory = TaskMemoryFromGenericPayload(Data.QueryParams.CustomDelegatePayload);
188 | if (!myMemory)
189 | return;
190 |
191 | auto ownerComp = myMemory->Metadata.OwnerComp.Get();
192 |
193 | // Store query results:
194 | myMemory->QueryResults = Data;
195 |
196 | // Validate results:
197 | if (!Data.PathSolutionOptimized.Num())
198 | {
199 | if (bTeleportToDestinationUponFailure && ownerComp)
200 | {
201 | TeleportAndExit(*ownerComp, false);
202 | myMemory->QueryResults.QueryStatus = EDonNavigationQueryStatus::Success;
203 | }
204 | else
205 | {
206 | UE_LOG(DoNNavigationLog, Log, TEXT("Found empty pathsolution in Fly To node. Aborting task..."));
207 | myMemory->QueryResults.QueryStatus = EDonNavigationQueryStatus::Failure;
208 | }
209 |
210 | return;
211 | }
212 |
213 | // Inform pawn owner that we're about to start locomotion!
214 | if (myMemory->bIsANavigator)
215 | {
216 | if (!ownerComp)
217 | return;
218 |
219 | APawn* pawn = ownerComp->GetAIOwner()->GetPawn();
220 |
221 | IDonNavigator::Execute_OnLocomotionBegin(pawn);
222 |
223 | //UE_LOG(DoNNavigationLog, Verbose, TEXT("Segment 0"));
224 | IDonNavigator::Execute_OnNextSegment(pawn, myMemory->QueryResults.PathSolutionOptimized[0]);
225 | }
226 |
227 | }
228 |
229 | void UBTTask_FlyTo::Pathfinding_OnDynamicCollisionAlert(const FDonNavigationDynamicCollisionPayload& Data)
230 | {
231 | auto myMemory = TaskMemoryFromGenericPayload(Data.CustomDelegatePayload);
232 | if (!myMemory)
233 | return;
234 |
235 | myMemory->bSolutionInvalidatedByDynamicObstacle = true;
236 |
237 | }
238 |
239 | void UBTTask_FlyTo::TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
240 | {
241 | FBT_FlyToTarget* myMemory = (FBT_FlyToTarget*)NodeMemory;
242 |
243 | APawn* pawn = OwnerComp.GetAIOwner()->GetPawn();
244 | NavigationManager = UDonNavigationHelper::DonNavigationManagerForActor(pawn);
245 |
246 | if (EDonNavigationQueryStatus::InProgress == myMemory->QueryResults.QueryStatus)
247 | return;
248 |
249 | switch (myMemory->QueryResults.QueryStatus)
250 | {
251 |
252 | case EDonNavigationQueryStatus::Success:
253 |
254 | // Is our path solution no longer valid?
255 | if (myMemory->bSolutionInvalidatedByDynamicObstacle)
256 | {
257 | NavigationManager->StopListeningToDynamicCollisionsForPath(myMemory->DynamicCollisionListener, myMemory->QueryResults);
258 |
259 | // Recalculate path (a dynamic obstacle has probably come out of nowhere and invalidated our current solution)
260 | EBTNodeResult::Type bRes = SchedulePathfindingRequest(OwnerComp, NodeMemory);
261 | if (bRes == EBTNodeResult::Failed)
262 | FinishLatentTask(OwnerComp, EBTNodeResult::Failed);
263 |
264 | break;
265 | }
266 |
267 | if (myMemory->bTargetLocationChanged)
268 | {
269 | EBTNodeResult::Type bRes = SchedulePathfindingRequest(OwnerComp, NodeMemory);
270 | if (bRes == EBTNodeResult::Failed)
271 | FinishLatentTask(OwnerComp, EBTNodeResult::Failed);
272 |
273 | break;
274 | }
275 |
276 | // Move along the path solution towards our goal:
277 | TickPathNavigation(OwnerComp, myMemory, DeltaSeconds);
278 |
279 | break;
280 |
281 | // For advanced usecases we could support partial path traversal, etc (so we slowly progress towards the goal
282 | // with each cycle of query-timeout->partial-reschedule->partial-navigate->query-timeout->partial-reschedule, etc)
283 | // but for now, let's just keep things simple.
284 |
285 | case EDonNavigationQueryStatus::QueryHasNoSolution:
286 | case EDonNavigationQueryStatus::TimedOut:
287 | case EDonNavigationQueryStatus::Failure:
288 | HandleTaskFailureAndExit(OwnerComp, NodeMemory);
289 | break;
290 |
291 | default: // currently covers "Unscheduled" which in turn is acting as the "In Progress" state in the current flow
292 | if (!NavigationManager->HasTask(pawn))
293 | HandleTaskFailureAndExit(OwnerComp, NodeMemory);
294 | }
295 | }
296 |
297 | void UBTTask_FlyTo::TickPathNavigation(UBehaviorTreeComponent& OwnerComp, FBT_FlyToTarget* MyMemory, float DeltaSeconds)
298 | {
299 | const auto& queryResults = MyMemory->QueryResults;
300 |
301 | APawn* pawn = OwnerComp.GetAIOwner()->GetPawn();
302 |
303 | if (DebugParams.bVisualizePawnAsVoxels)
304 | NavigationManager->Debug_DrawVoxelCollisionProfile(Cast(pawn->GetRootComponent()));
305 |
306 | if (!queryResults.PathSolutionOptimized.IsValidIndex(MyMemory->solutionTraversalIndex))
307 | {
308 | HandleTaskFailureAndExit(OwnerComp, (uint8*) (MyMemory)); // observed after recent multi-threading rewrite. Need to watch this branch closely and understand why it occurs!
309 | return;
310 | }
311 |
312 | FVector flightDirection = queryResults.PathSolutionOptimized[MyMemory->solutionTraversalIndex] - pawn->GetActorLocation();
313 |
314 | //auto navigator = Cast(pawn);
315 |
316 | // Add movement input:
317 | if (MyMemory->bIsANavigator)
318 | {
319 | // Customized movement handling for advanced users:
320 | IDonNavigator::Execute_AddMovementInputCustom(pawn, flightDirection, 1.f);
321 | }
322 | else
323 | {
324 | // Default movement (handled by Pawn or Character class)
325 | pawn->AddMovementInput(flightDirection, 1.f);
326 | }
327 |
328 | //UE_LOG(DoNNavigationLog, Verbose, TEXT("Segment %d Distance: %f"), MyMemory->solutionTraversalIndex, flightDirection.Size());
329 |
330 | // Reached next segment:
331 | if (flightDirection.Size() <= MinimumProximityRequired)
332 | {
333 | // Goal reached?
334 | if (MyMemory->solutionTraversalIndex == queryResults.PathSolutionOptimized.Num() - 1)
335 | {
336 | auto controller = pawn->GetController();
337 | auto blackboard = controller ? controller->FindComponentByClass() : NULL;
338 | if (blackboard)
339 | {
340 | blackboard->SetValueAsBool(FlightResultKey.SelectedKeyName, true);
341 | blackboard->SetValueAsBool(KeyToFlipFlopWhenTaskExits.SelectedKeyName, !blackboard->GetValueAsBool(KeyToFlipFlopWhenTaskExits.SelectedKeyName));
342 | }
343 |
344 | // Unregister all dynamic collision listeners. We've completed our task and are no longer interested in listening to these:
345 | NavigationManager->StopListeningToDynamicCollisionsForPath(MyMemory->DynamicCollisionListener, queryResults);
346 |
347 | // Inform the pawn owner that we're stopping locomotion (having reached the destination!)
348 | if (MyMemory->bIsANavigator)
349 | IDonNavigator::Execute_OnLocomotionEnd(pawn, true /*success*/);
350 |
351 | FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
352 |
353 | return;
354 | }
355 | else
356 | {
357 | MyMemory->solutionTraversalIndex++;
358 |
359 | // Because we just completed a segment, we should stop listening to collisions on the previous voxel.
360 | // If not, a pawn may needlessly recalculate its solution when a obstacle far behind it intrudes on a voxel it has already visited.
361 | if (!NavigationManager->bIsUnbound && queryResults.VolumeSolutionOptimized.IsValidIndex(MyMemory->solutionTraversalIndex - 1))
362 | NavigationManager->StopListeningToDynamicCollisionsForPathIndex(MyMemory->DynamicCollisionListener, queryResults, MyMemory->solutionTraversalIndex - 1);
363 |
364 | if (MyMemory->bIsANavigator)
365 | {
366 | if (!MyMemory->Metadata.OwnerComp.IsValid()) // edge case identified during high-speed time dilation. Need to gain a better understanding of exactly what triggers this issue.
367 | {
368 | HandleTaskFailureAndExit(OwnerComp, (uint8*)MyMemory);
369 | return;
370 | }
371 |
372 | if (queryResults.PathSolutionOptimized.IsValidIndex(MyMemory->solutionTraversalIndex))
373 | {
374 | FVector nextPoint = queryResults.PathSolutionOptimized[MyMemory->solutionTraversalIndex];
375 | //UE_LOG(DoNNavigationLog, Verbose, TEXT("Segment %d, %s"), MyMemory->solutionTraversalIndex, *nextPoint.ToString());
376 |
377 | IDonNavigator::Execute_OnNextSegment(pawn, nextPoint);
378 | }
379 | }
380 |
381 | }
382 | }
383 | }
384 |
385 |
386 | void UBTTask_FlyTo::OnTaskFinished(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTNodeResult::Type TaskResult)
387 | {
388 | FBT_FlyToTarget* myMemory = (FBT_FlyToTarget*)NodeMemory;
389 |
390 | UBlackboardComponent* BlackboardComp = OwnerComp.GetBlackboardComponent();
391 | if (ensure(BlackboardComp) && myMemory->BBObserverDelegateHandle.IsValid())
392 | BlackboardComp->UnregisterObserver(FlightLocationKey.GetSelectedKeyID(), myMemory->BBObserverDelegateHandle);
393 |
394 | myMemory->BBObserverDelegateHandle.Reset();
395 |
396 | Super::OnTaskFinished(OwnerComp, NodeMemory, TaskResult);
397 | }
398 |
399 | EBTNodeResult::Type UBTTask_FlyTo::HandleTaskFailure(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, UBlackboardComponent* Blackboard)
400 | {
401 | AbortPathfindingRequest(OwnerComp, NodeMemory);
402 |
403 | auto myMemory = NodeMemory ? reinterpret_cast (NodeMemory) : nullptr;
404 | if (!Blackboard)
405 | return EBTNodeResult::Failed;
406 |
407 | bool bOverallStatus = false;
408 | if (bTeleportToDestinationUponFailure)
409 | {
410 | const bool bWrapUpLatentTask = false;
411 | bOverallStatus = TeleportAndExit(OwnerComp, bWrapUpLatentTask);
412 | }
413 |
414 | Blackboard->SetValueAsBool(FlightResultKey.SelectedKeyName, false);
415 | Blackboard->SetValueAsBool(KeyToFlipFlopWhenTaskExits.SelectedKeyName, !Blackboard->GetValueAsBool(KeyToFlipFlopWhenTaskExits.SelectedKeyName));
416 |
417 | if (myMemory->bIsANavigator)
418 | IDonNavigator::Execute_OnLocomotionEnd(OwnerComp.GetAIOwner()->GetPawn(), bOverallStatus);
419 |
420 | if(bOverallStatus)
421 | return EBTNodeResult::Succeeded;
422 | else
423 | return EBTNodeResult::Failed;
424 | }
425 |
426 | void UBTTask_FlyTo::HandleTaskFailureAndExit(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
427 | {
428 | auto pawn = OwnerComp.GetAIOwner()->GetPawn();
429 | UBlackboardComponent* blackboard = pawn && pawn->GetController() ? pawn->GetController()->FindComponentByClass() : nullptr;
430 |
431 | if (HandleTaskFailure(OwnerComp, NodeMemory, blackboard) == EBTNodeResult::Failed)
432 | FinishLatentTask(OwnerComp, EBTNodeResult::Failed);
433 | else
434 | FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
435 | }
436 |
437 | EBlackboardNotificationResult UBTTask_FlyTo::OnBlackboardValueChange(const UBlackboardComponent& Blackboard, FBlackboard::FKey ChangedKeyID)
438 | {
439 | if (!bRecalcPathOnDestinationChanged)
440 | return EBlackboardNotificationResult::RemoveObserver;
441 |
442 | UBehaviorTreeComponent* BehaviorComp = Cast(Blackboard.GetBrainComponent());
443 | if (BehaviorComp == nullptr)
444 | return EBlackboardNotificationResult::RemoveObserver;
445 |
446 | uint8* RawMemory = BehaviorComp->GetNodeMemory(this, BehaviorComp->FindInstanceContainingNode(this));
447 | FBT_FlyToTarget* myMemory = reinterpret_cast(RawMemory);
448 |
449 | const EBTTaskStatus::Type TaskStatus = BehaviorComp->GetTaskStatus(this);
450 | if (TaskStatus != EBTTaskStatus::Active)
451 | {
452 | UE_VLOG(BehaviorComp, LogBehaviorTree, Error, TEXT("BT MoveTo \'%s\' task observing BB entry while no longer being active!"), *GetNodeName());
453 |
454 | // resetting BBObserverDelegateHandle without unregistering observer since
455 | // returning EBlackboardNotificationResult::RemoveObserver here will take care of that for us
456 | myMemory->BBObserverDelegateHandle.Reset();
457 |
458 | return EBlackboardNotificationResult::RemoveObserver;
459 | }
460 |
461 | if (myMemory != nullptr)
462 | {
463 | const FVector flightDestination = Blackboard.GetValueAsVector(FlightLocationKey.SelectedKeyName);
464 |
465 | if (!myMemory->TargetLocation.Equals(flightDestination, RecalculatePathTolerance))
466 | myMemory->bTargetLocationChanged = true;
467 | }
468 |
469 | return EBlackboardNotificationResult::ContinueObserving;
470 | }
471 |
472 | EBTNodeResult::Type UBTTask_FlyTo::AbortTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
473 | {
474 | // safely abort nav task before we leave
475 | AbortPathfindingRequest(OwnerComp, NodeMemory);
476 |
477 | APawn* pawn = OwnerComp.GetAIOwner()->GetPawn();
478 | FBT_FlyToTarget* myMemory = (FBT_FlyToTarget*)NodeMemory;
479 |
480 | // Notify locomotion state:
481 | if (myMemory->QueryResults.PathSolutionOptimized.Num() && myMemory->bIsANavigator && pawn)
482 | IDonNavigator::Execute_OnLocomotionAbort(pawn);
483 |
484 | return Super::AbortTask(OwnerComp, NodeMemory);
485 | }
486 |
487 |
488 | FString UBTTask_FlyTo::GetStaticDescription() const
489 | {
490 | FString ReturnDesc = Super::GetStaticDescription();
491 |
492 | ReturnDesc += FString::Printf(TEXT("\n%s: %s \n"), *GET_MEMBER_NAME_CHECKED(UBTTask_FlyTo, FlightLocationKey).ToString(), *FlightLocationKey.SelectedKeyName.ToString());
493 | ReturnDesc += FString("\nDebug Visualization:");
494 | ReturnDesc += FString::Printf(TEXT("Raw Path: %d \n"), DebugParams.VisualizeRawPath);
495 | ReturnDesc += FString::Printf(TEXT("Optimized Path: %d \n"), DebugParams.VisualizeOptimizedPath);
496 |
497 | return FString::Printf(TEXT("%s"), *ReturnDesc);
498 | }
499 |
500 | void UBTTask_FlyTo::DescribeRuntimeValues(const UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTDescriptionVerbosity::Type Verbosity, TArray& Values) const
501 | {
502 | Super::DescribeRuntimeValues(OwnerComp, NodeMemory, Verbosity, Values);
503 | }
504 |
505 | uint16 UBTTask_FlyTo::GetInstanceMemorySize() const
506 | {
507 | return sizeof(FBT_FlyToTarget);
508 | }
509 |
510 | #if WITH_EDITOR
511 |
512 | FName UBTTask_FlyTo::GetNodeIconName() const
513 | {
514 | return FName("BTEditor.Graph.BTNode.Task.Wait.Icon");
515 | }
516 |
517 | #endif // WITH_EDITOR
518 |
519 | bool UBTTask_FlyTo::TeleportAndExit(UBehaviorTreeComponent& OwnerComp, bool bWrapUpLatentTask /*= true*/)
520 | {
521 | bool bTeleportSuccess = false;
522 | auto pawn = OwnerComp.GetAIOwner()->GetPawn();
523 | auto blackboard = pawn ? pawn->GetController()->FindComponentByClass() : nullptr;
524 |
525 | if (blackboard)
526 | {
527 | FVector flightDestination = blackboard->GetValueAsVector(FlightLocationKey.SelectedKeyName);
528 | NavigationManager = UDonNavigationHelper::DonNavigationManagerForActor(pawn);
529 |
530 | bool bLocationValid = !NavigationManager->IsLocationBeneathLandscape(flightDestination);
531 | if(bLocationValid)
532 | {
533 | FVector flightDestination = blackboard->GetValueAsVector(FlightLocationKey.SelectedKeyName);
534 | pawn->SetActorLocation(flightDestination, false);
535 | GEngine->AddOnScreenDebugMessage(-1, 15.f, FColor::White, FString::Printf(TEXT("%s teleported, being unable to find pathfind aerially!"), pawn ? *pawn->GetName() : *FString("")));
536 | bTeleportSuccess = true;
537 | }
538 | }
539 |
540 | if (bWrapUpLatentTask)
541 | {
542 | if (bTeleportSuccess)
543 | FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded);
544 | else
545 | FinishLatentTask(OwnerComp, EBTNodeResult::Failed);
546 | }
547 |
548 | return bTeleportSuccess;
549 | }
550 |
551 | //float UBTTask_FlyTo::LastRequestTimestamp;
552 | const float UBTTask_FlyTo::RequestThrottleInterval = 0.25f;
553 |
--------------------------------------------------------------------------------
/Source/DonAINavigation/Private/DonAINavigation.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | #include "DonAINavigationPrivatePCH.h"
16 |
17 | DEFINE_LOG_CATEGORY(DoNNavigationLog);
18 |
19 | class FDonAINavigation : public IDonAINavigation
20 | {
21 | /** IModuleInterface implementation */
22 | virtual void StartupModule() override;
23 | virtual void ShutdownModule() override;
24 | };
25 |
26 | IMPLEMENT_MODULE( FDonAINavigation, DonAINavigation )
27 |
28 |
29 |
30 | void FDonAINavigation::StartupModule()
31 | {
32 | // This code will execute after your module is loaded into memory (but after global variables are initialized, of course.)
33 | }
34 |
35 |
36 | void FDonAINavigation::ShutdownModule()
37 | {
38 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
39 | // we call this function before unloading the module.
40 | }
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Source/DonAINavigation/Private/DonAINavigationPrivatePCH.h:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | #include "CoreUObject.h"
16 | #include "Engine.h"
17 |
18 | // You should place include statements to your module's private header files here. You only need to
19 | // add includes for headers that are used in most of your module's source files though.
20 | #include "IDonAINavigation.h"
21 |
--------------------------------------------------------------------------------
/Source/DonAINavigation/Private/DonNavigationHelper.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | #include "DonNavigationHelper.h"
16 | #include "DonAINavigationPrivatePCH.h"
17 |
18 | ADonNavigationManager* UDonNavigationHelper::DonNavigationManager(UObject* WorldContextObject)
19 | {
20 | UWorld* const World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull);
21 | if (!World)
22 | return NULL;
23 |
24 | for (TActorIterator It(World, ADonNavigationManager::StaticClass()); It; ++It)
25 | {
26 | return *It;
27 | }
28 |
29 | return NULL;
30 | }
31 |
32 | ADonNavigationManager* UDonNavigationHelper::DonNavigationManagerForActor(const AActor *Actor)
33 | {
34 | if (!Actor)
35 | return nullptr;
36 |
37 | for (TActorIterator It(Actor->GetWorld(), ADonNavigationManager::StaticClass()); It; ++It) {
38 | const ADonNavigationManager *Mgr = *It;
39 | if (Mgr->IsLocationWithinNavigableWorld(Actor->GetActorLocation()))
40 | return *It;
41 | }
42 |
43 | return nullptr;
44 | }
45 |
--------------------------------------------------------------------------------
/Source/DonAINavigation/Private/DonNavigationManagerUnbound.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | #include "DonNavigationManagerUnbound.h"
16 | #include "DonAINavigationPrivatePCH.h"
17 |
18 | ADonNavigationManagerUnbound::ADonNavigationManagerUnbound(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
19 | {
20 | bIsUnbound = true;
21 | }
22 |
23 | void ADonNavigationManagerUnbound::BeginPlay()
24 | {
25 | Super::BeginPlay();
26 | }
27 |
28 | bool ADonNavigationManagerUnbound::PrepareSolution(FDonNavigationQueryTask& Task)
29 | {
30 | if (!bIsUnbound)
31 | return Super::PrepareSolution(Task);
32 |
33 | auto& data = Task.Data;
34 |
35 | // a rare edgecase, but worth handling gracefully in any case
36 | if (data.OriginVolumeCenter == data.DestinationVolumeCenter)
37 | {
38 | data.PathSolutionRaw.Add(data.Origin);
39 | data.PathSolutionRaw.Add(data.Destination);
40 |
41 | return true;
42 | }
43 |
44 | // The trajectory map operates in reverse, so start from the destination:
45 | data.PathSolutionRaw.Insert(data.Destination, 0);
46 |
47 | // Work our way back from destination to origin while generating a linear path solution list
48 | bool originFound = false;
49 | auto nextLocation = data.VolumeVsGoalTrajectoryMap_Unbound.Find(data.DestinationVolumeCenter);
50 |
51 | while (nextLocation)
52 | {
53 | if (data.PathSolutionRaw.Contains(*nextLocation))
54 | break; // Note:- this was introduced alongisde the bugfix to the bound manager to prevent infinte loops in PathSolutionFromVolumeTrajectoryMap
55 |
56 | data.PathSolutionRaw.Insert((*nextLocation), 0);
57 |
58 | if (*nextLocation == data.OriginVolumeCenter)
59 | {
60 | originFound = true;
61 | break;
62 | }
63 |
64 | nextLocation = data.VolumeVsGoalTrajectoryMap_Unbound.Find(*nextLocation);
65 | }
66 |
67 | return originFound;
68 | }
69 |
70 |
71 | void ADonNavigationManagerUnbound::TickNavigationSolver(FDonNavigationQueryTask& task)
72 | {
73 | if (!bIsUnbound)
74 | {
75 | Super::TickNavigationSolver(task);
76 |
77 | return;
78 | }
79 |
80 | auto& data = task.Data;
81 |
82 | data.SolverIterationCount++;
83 |
84 | if (!data.Frontier_Unbound.empty())
85 | {
86 | // Move towards goal by fetching the "best neighbor" of the previous volume from the Frontier priority queue
87 | // The best neighbor is defined as the node most likely to lead us towards the goal
88 | FVector currentVector = data.Frontier_Unbound.get();
89 |
90 | // Have we reached the goal?
91 | if (currentVector == data.DestinationVolumeCenter)
92 | {
93 | data.bGoalFound = true;
94 | return;
95 | }
96 |
97 | // Discover all neighbors for current volume:
98 | const auto& neighbors = NeighborsAsVectors(currentVector);
99 |
100 | // Evaluate each neighbor for suitability, assign points, add to Frontier
101 | for (auto neighbor : neighbors)
102 | {
103 | ExpandFrontierTowardsTarget(task, currentVector, neighbor);
104 | }
105 | }
106 | }
107 |
108 | TArray ADonNavigationManagerUnbound::NeighborsAsVectors(FVector Location)
109 | {
110 | TArray neighbors;
111 | neighbors.Reserve(Volume6DOF + VolumeImplicitDOF);
112 |
113 | // 6 DOF neighbors (Direct neighbors)
114 | for (int32 i = 0; i < Volume6DOF; i++)
115 | {
116 | FVector neighbor = Location + VoxelSize * FVector(x6DOFCoords[i], y6DOFCoords[i], z6DOFCoords[i]);
117 |
118 | neighbors.Add(neighbor);
119 | }
120 |
121 | // Implicit:
122 |
123 | // X
124 |
125 | if (CanNavigate(LocationAtId(Location, 1, 0, 0)) && CanNavigate(LocationAtId(Location, 0, 0, 1)))
126 | neighbors.Add(LocationAtId(Location, 1, 0, 1));
127 |
128 | if (CanNavigate(LocationAtId(Location, -1, 0, 0)) && CanNavigate(LocationAtId(Location, 0, 0, 1)))
129 | neighbors.Add(LocationAtId(Location, -1, 0, 1));
130 |
131 | if (CanNavigate(LocationAtId(Location, 1, 0, 0)) && CanNavigate(LocationAtId(Location, 0, 0, -1)))
132 | neighbors.Add(LocationAtId(Location, 1, 0, -1));
133 |
134 | if (CanNavigate(LocationAtId(Location, -1, 0, 0)) && CanNavigate(LocationAtId(Location, 0, 0, -1)))
135 | neighbors.Add(LocationAtId(Location, -1, 0, -1));
136 |
137 | //Y
138 | if (CanNavigate(LocationAtId(Location, 0, 1, 0)) && CanNavigate(LocationAtId(Location, 0, 0, 1)))
139 | neighbors.Add(LocationAtId(Location, 0, 1, 1));
140 |
141 | if (CanNavigate(LocationAtId(Location, 0, -1, 0)) && CanNavigate(LocationAtId(Location, 0, 0, 1)))
142 | neighbors.Add(LocationAtId(Location, 0, -1, 1));
143 |
144 | if (CanNavigate(LocationAtId(Location, 0, 1, 0)) && CanNavigate(LocationAtId(Location, 0, 0, -1)))
145 | neighbors.Add(LocationAtId(Location, 0, 1, -1));
146 |
147 | if (CanNavigate(LocationAtId(Location, 0, -1, 0)) && CanNavigate(LocationAtId(Location, 0, 0, -1)))
148 | neighbors.Add(LocationAtId(Location, 0, -1, -1));
149 |
150 | //Z
151 | if (CanNavigate(LocationAtId(Location, 1, 0, 0)) && CanNavigate(LocationAtId(Location, 0, 1, 0)))
152 | neighbors.Add(LocationAtId(Location, 1, 1, 0));
153 |
154 | if (CanNavigate(LocationAtId(Location, -1, 0, 0)) && CanNavigate(LocationAtId(Location, 0, 1, 0)))
155 | neighbors.Add(LocationAtId(Location, -1, 1, 0));
156 |
157 | if (CanNavigate(LocationAtId(Location, 1, 0, 0)) && CanNavigate(LocationAtId(Location, 0, -1, 0)))
158 | neighbors.Add(LocationAtId(Location, 1, -1, 0));
159 |
160 | if (CanNavigate(LocationAtId(Location, -1, 0, 0)) && CanNavigate(LocationAtId(Location, 0, -1, 0)))
161 | neighbors.Add(LocationAtId(Location, -1, -1, 0));
162 |
163 | return neighbors;
164 | }
165 |
166 | void ADonNavigationManagerUnbound::ExpandFrontierTowardsTarget(FDonNavigationQueryTask& Task, FVector Current, FVector Neighbor)
167 | {
168 | if (!CanNavigateByCollisionProfile(Neighbor, Task.Data.VoxelCollisionProfile))
169 | return;
170 |
171 | float SegmentDist = VoxelSize;
172 |
173 | uint32 newCost = *Task.Data.VolumeVsCostMap_Unbound.Find(Current) + SegmentDist;
174 | uint32* volumeCost = Task.Data.VolumeVsCostMap_Unbound.Find(Neighbor);
175 |
176 | if (!volumeCost || newCost < *volumeCost)
177 | {
178 | Task.Data.VolumeVsGoalTrajectoryMap_Unbound.Add(Neighbor, Current);
179 | Task.Data.VolumeVsCostMap_Unbound.Add(Neighbor, newCost);
180 |
181 | float heuristic = FVector::Dist(Neighbor, Task.Data.Destination);
182 | uint32 priority = newCost + heuristic;
183 |
184 | Task.Data.Frontier_Unbound.put(Neighbor, priority);
185 | }
186 | }
--------------------------------------------------------------------------------
/Source/DonAINavigation/Private/DonNavigatorInterface.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | #pragma once
16 |
17 | #include "DonNavigatorInterface.h"
18 | #include "DonAINavigationPrivatePCH.h"
19 |
20 |
21 | UDonNavigator::UDonNavigator(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
22 | {
23 |
24 | }
--------------------------------------------------------------------------------
/Source/DonAINavigation/Private/EnvironmentQuery/DonEnvQueryTest_Navigation.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Contributors: This EQS code was kindly contributed by Vladimir Ivanov (Github @ArCorvus)
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
8 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
10 | //
11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
15 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 |
17 | #include "DonEnvQueryTest_Navigation.h"
18 | #include "../DonAINavigationPrivatePCH.h"
19 |
20 | #include "DonNavigationManager.h"
21 | #include "AIController.h"
22 | #include "EnvironmentQuery/Items/EnvQueryItemType_VectorBase.h"
23 | #include "EnvironmentQuery/Contexts/EnvQueryContext_Querier.h"
24 | #include "EnvironmentQuery/Items/EnvQueryItemType_Point.h"
25 |
26 |
27 |
28 | UDonEnvQueryTest_Navigation::UDonEnvQueryTest_Navigation(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
29 | {
30 | Cost = EEnvTestCost::High;
31 | TestPurpose = EEnvTestPurpose::Filter;
32 | FilterType = EEnvTestFilterType::Match;
33 | ValidItemType = UEnvQueryItemType_VectorBase::StaticClass();
34 | //Context = UEnvQueryContext_Querier::StaticClass();
35 | SetWorkOnFloatValues(false);
36 | RandomLocationMaxAttempts = 3;
37 | RandomLocationRadius = 100.f;
38 | }
39 |
40 | void UDonEnvQueryTest_Navigation::RunTest(FEnvQueryInstance& QueryInstance) const
41 | {
42 | UObject* QueryOwner = QueryInstance.Owner.Get();
43 | if (QueryOwner == nullptr) {
44 | return;
45 | }
46 |
47 | const AAIController *AICon = Cast(QueryOwner);
48 | AActor *ownerActor = nullptr;
49 | if (AICon) {
50 | ownerActor = AICon->GetPawn();
51 | }
52 | else {
53 | ownerActor = Cast(QueryOwner);
54 | }
55 | if (!ownerActor)
56 | return;
57 |
58 | BoolValue.BindData(QueryOwner, QueryInstance.QueryID);
59 | bool bWantsValid = BoolValue.GetValue();
60 |
61 | ADonNavigationManager * DonNav = nullptr;
62 |
63 | for (TActorIterator It(ownerActor->GetWorld(), ADonNavigationManager::StaticClass()); It; ++It) {
64 | const ADonNavigationManager *NavMgr = *It;
65 | if (NavMgr->IsLocationWithinNavigableWorld(ownerActor->GetActorLocation())) {
66 | DonNav = *It;
67 | break;
68 | }
69 | }
70 |
71 | if (!DonNav)
72 | return;
73 |
74 | UEnvQueryItemType_Point* ItemTypeCDO = nullptr;
75 |
76 | if (bSearchRandomLocation) {
77 | const auto &itemType = QueryInstance.ItemType;
78 | if (itemType && itemType->IsChildOf(UEnvQueryItemType_Point::StaticClass())) {
79 | ItemTypeCDO = QueryInstance.ItemType->GetDefaultObject();
80 | }
81 | }
82 |
83 | for (FEnvQueryInstance::ItemIterator It(this, QueryInstance); It; ++It) {
84 | const FVector ItemLocation = GetItemLocation(QueryInstance, It.GetIndex());
85 |
86 | bool ItemValid = false;
87 | ItemValid = DonNav->CanNavigate(ItemLocation);
88 | FVector NavLocation;
89 | if (bSearchRandomLocation) {
90 | if (!ItemValid) {
91 | NavLocation = DonNav->FindRandomPointAroundOriginInNavWorld(ownerActor, ItemLocation, RandomLocationRadius, ItemValid, -1.f, 15.f, RandomLocationMaxAttempts);
92 | }
93 |
94 | if (ItemTypeCDO) {
95 | ItemTypeCDO->SetItemNavLocation(It.GetItemData(), FNavLocation(NavLocation));
96 | }
97 | }
98 |
99 | It.SetScore(TestPurpose, FilterType, ItemValid, bWantsValid);
100 | }
101 | }
102 |
103 | FText UDonEnvQueryTest_Navigation::GetDescriptionTitle() const
104 | {
105 | return FText::FromString(FString::Printf(TEXT("Don Navigation: Item is navigable")));
106 | }
107 |
108 | FText UDonEnvQueryTest_Navigation::GetDescriptionDetails() const
109 | {
110 | return FText::Format(FText::FromString("{0}\nDoN Location is navigable"),
111 | DescribeFloatTestParams());
112 | }
--------------------------------------------------------------------------------
/Source/DonAINavigation/Private/Multithreading/DonNavigationWorker.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | #include "Multithreading/DonNavigationWorker.h"
16 | #include "../DonAINavigationPrivatePCH.h"
17 |
18 | #include "DonNavigationManager.h"
19 |
20 | FDonNavigationWorker::FDonNavigationWorker()
21 | {
22 |
23 | }
24 |
25 | FDonNavigationWorker::FDonNavigationWorker(ADonNavigationManager* Manager, int32 MaxPathSolverIterations, int32 MaxCollisionSolverIterations)
26 | : Manager(Manager),
27 | MaxPathSolverIterations(MaxPathSolverIterations),
28 | MaxCollisionSolverIterations(MaxCollisionSolverIterations)
29 | {
30 | Thread = FRunnableThread::Create(this, TEXT("DonNavigationWorker"), 0U, TPri_BelowNormal);
31 | }
32 |
33 | FDonNavigationWorker::~FDonNavigationWorker()
34 | {
35 | delete Thread;
36 |
37 | Thread = NULL;
38 | }
39 |
40 | void FDonNavigationWorker::ShutDown()
41 | {
42 | Stop();
43 |
44 | Thread->WaitForCompletion();
45 | }
46 |
47 | bool FDonNavigationWorker::Init()
48 | {
49 | if (Manager)
50 | {
51 | UE_LOG(DoNNavigationLog, Log, TEXT("FDonNavigationWorker thread started"));
52 |
53 | return true;
54 | }
55 | return false;
56 | }
57 |
58 | uint32 FDonNavigationWorker::Run()
59 | {
60 | FPlatformProcess::Sleep(0.03f);
61 |
62 | while (StopTaskCounter.GetValue() == 0)
63 | {
64 | //Manager->ReceiveAsyncAbortRequests();
65 | Manager->ReceiveAsyncNavigationTasks();
66 | Manager->ReceiveAsyncCollisionTasks();
67 |
68 | SolveNavigationTasks();
69 |
70 | //FPlatformProcess::Sleep(0.2f);
71 | }
72 | return 0;
73 | }
74 |
75 | void FDonNavigationWorker::Stop()
76 | {
77 | StopTaskCounter.Increment();
78 | }
79 |
80 | void FDonNavigationWorker::SolveNavigationTasks()
81 | {
82 | Manager->TickScheduledPathfindingTasks_Safe(0.f, MaxPathSolverIterations);
83 |
84 | Manager->TickScheduledCollisionTasks_Safe(0.f, MaxCollisionSolverIterations);
85 | }
--------------------------------------------------------------------------------
/Source/DonAINavigation/Public/EnvironmentQuery/DonEnvQueryTest_Navigation.h:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Contributors: This EQS code was kindly contributed by Vladimir Ivanov (Github @ArCorvus)
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
8 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
10 | //
11 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
15 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 |
17 | // Venu's Note:- This EQS code was kindly contributed by Vladimir Ivanov (Github @ArCorvus)
18 |
19 | #pragma once
20 |
21 | #include "CoreMinimal.h"
22 | #include "EnvironmentQuery/EnvQueryTest.h"
23 | #include "DonEnvQueryTest_Navigation.generated.h"
24 |
25 | /**
26 | *
27 | */
28 | UCLASS()
29 | class DONAINAVIGATION_API UDonEnvQueryTest_Navigation : public UEnvQueryTest
30 | {
31 | GENERATED_BODY()
32 |
33 | public:
34 | UDonEnvQueryTest_Navigation(const FObjectInitializer& ObjectInitializer);
35 | virtual void RunTest(FEnvQueryInstance& QueryInstance) const override;
36 |
37 |
38 | public:
39 | /** */
40 | //UPROPERTY(EditDefaultsOnly, Category = Trace)
41 | //TSubclassOf Context;
42 |
43 | /** Search for random location nearby */
44 | UPROPERTY(EditDefaultsOnly, Category = Test, meta = (InlineEditConditionToggle))
45 | uint32 bSearchRandomLocation : 1;
46 |
47 | /** Number of Attempts to find random location nearby if Item's location is not valid */
48 | UPROPERTY(EditDefaultsOnly, Category = Test, meta = (EditCondition = "bSearchRandomLocation"))
49 | int32 RandomLocationMaxAttempts;
50 |
51 | /** Find Random location radius */
52 | UPROPERTY(EditDefaultsOnly, Category = Test, meta = (EditCondition = "bSearchRandomLocation"))
53 | float RandomLocationRadius;
54 |
55 | private:
56 | virtual FText GetDescriptionTitle() const override;
57 | virtual FText GetDescriptionDetails() const override;
58 |
59 | };
60 |
--------------------------------------------------------------------------------
/Source/DonAINavigation/Public/IDonAINavigation.h:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright(c) 2015 Venugopalan Sreedharan
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"),
6 | // to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
8 | //
9 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | //
11 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
13 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 |
15 | #pragma once
16 |
17 | #include "ModuleManager.h"
18 |
19 | DECLARE_LOG_CATEGORY_EXTERN(DoNNavigationLog, Log, All);
20 |
21 | /**
22 | * The public interface to this module
23 | */
24 | class IDonAINavigation : public IModuleInterface
25 | {
26 |
27 | public:
28 |
29 | /**
30 | * Singleton-like access to this module's interface. This is just for convenience!
31 | * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
32 | *
33 | * @return Returns singleton instance, loading the module on demand if needed
34 | */
35 | static inline IDonAINavigation& Get()
36 | {
37 | return FModuleManager::LoadModuleChecked< IDonAINavigation >( "DonAINavigation" );
38 | }
39 |
40 | /**
41 | * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true.
42 | *
43 | * @return True if the module is loaded and ready to use
44 | */
45 | static inline bool IsAvailable()
46 | {
47 | return FModuleManager::Get().IsModuleLoaded( "DonAINavigation" );
48 | }
49 | };
50 |
51 |
--------------------------------------------------------------------------------