Agrarsense
AgrarsenseGameModeBase.cpp
Go to the documentation of this file.
1// Copyright (c) 2023 FrostBit Software Lab at the Lapland University of Applied Sciences
2//
3// This work is licensed under the terms of the MIT license.
4// For a copy, see <https://opensource.org/licenses/MIT>.
5
7#include "AgrarsensePaths.h"
20
21#include "Kismet/GameplayStatics.h"
22#include "Kismet/KismetSystemLibrary.h"
23#include "Engine/World.h"
24#include "Engine.h"
25#include "TimerManager.h"
26
27#include "ROSIntegration/Classes/ROSIntegrationGameInstance.h"
28
32
33AAgrarsenseGameModeBase::AAgrarsenseGameModeBase(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
34{
35 PrimaryActorTick.bCanEverTick = false;
36}
37
38void AAgrarsenseGameModeBase::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
39{
40 Super::InitGame(MapName, Options, ErrorMessage);
41
43}
44
46{
47 Super::BeginPlay();
48
50
52}
53
54void AAgrarsenseGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
55{
56 Super::EndPlay(EndPlayReason);
57
58 if (EndPlayReason == EEndPlayReason::Type::Quit || EndPlayReason == EEndPlayReason::Type::EndPlayInEditor)
59 {
61
62 if (ROSCommands)
63 {
65 }
66
67 SimulatorLog::Log("Simulator shutting down. Goodbye.");
69 }
70
71#if WITH_EDITOR
72 if (EndPlayReason == EEndPlayReason::Type::EndPlayInEditor)
73 {
74 EditorEndPlayCleanup();
75 }
76#endif
77}
78
79#if WITH_EDITOR
80void AAgrarsenseGameModeBase::EditorEndPlayCleanup()
81{
82 // TODO: this is not good approach.
83 // Clear certain static variables at Unreal Editor EndPlay.
84 // This is not relevant for packaged builds as most of this data
85 // is meant to be kept in memory as long as the Simulator is running.
86 IActorInformation::ClearTMapEditor();
89 StartMessageLogged = false;
90 MapChanged = false;
91}
92#endif
93
95{
96 if (MapChanged)
97 {
98 // If map has already been changed, don't change it again.
99 return;
100 }
101
102 // Map names and their actual paths to check against
103 const TMap<FString, FString> MapLaunchArgs = {
104 { "-vindeln", "/Game/Agrarsense/Maps/Vindeln/Vindeln" },
105 { "-vindeln_dev", "/Game/Agrarsense/Maps/Vindeln/Vindeln_Dev" },
106 { "-rovaniemi", "/Game/Agrarsense/Maps/RovaniemiForest/RovaniemiForest" },
107 { "-rovaniemi_dev", "/Game/Agrarsense/Maps/RovaniemiForest/RovaniemiForest_Dev" },
108 { "-playground", "/Game/Agrarsense/Maps/Playground" }
109 };
110
111 const FString CommandLine = FCommandLine::Get();
112 FString MapName;
113
114 for (const auto& Pair : MapLaunchArgs)
115 {
116 if (FParse::Param(*CommandLine, *Pair.Key))
117 {
118 MapChanged = true;
119 MapName = Pair.Value;
120 break;
121 }
122 }
123
124 if (MapChanged)
125 {
126 // Chage map with minor delay.
127 FTimerHandle Handle;
128 GetWorld()->GetTimerManager().SetTimer(Handle, FTimerDelegate::CreateLambda([this, MapName]
129 {
130 if (IsValid(this))
131 {
132 UGameplayStatics::OpenLevel(GetWorld(), *MapName);
133 }
134 }), 0.300f, false);
135 }
136}
137
139{
141 {
143 SimulatorLog::Log("Simulator starting.");
144 StartMessageLogged = true;
145 }
146
147 UWorld* World = GetWorld();
148
149 // Setup Agrarsense settings
151 if (Settings)
152 {
153 Settings->Setup(World);
154 }
155
156 UROSIntegrationGameInstance* RosInstance = Cast<UROSIntegrationGameInstance>(World->GetGameInstance());
157 bool ROSConnected = RosInstance->IsROSConnected();
158
159 // Create UROSHandle UObject
160 if (!ROSHandle)
161 {
162 ROSHandle = NewObject<UROSHandler>();
163
164 if (ROSHandle)
165 {
166 ROSHandle->Setup(ROSConnected);
167 if (RosInstance->bConnectToROS && !ROSConnected && AttemptToLaunchROSBridge)
168 {
170
171 // Only try to attempt once.
172 // Otherwise if ROS is not connected, simulator attempts to launch ROS Bridge
173 // process each time map is changed.
175 }
176 }
177 }
178
179 // Spawn Lidar Mananger. This actor should always exists in the scene.
180 // This actor only ticks when there is Lidar sensor(s) in the world.
182 {
183 LidarManager = World->SpawnActor<ALidarManager>(LidarManagerClass);
184 }
185 else
186 {
187 UE_LOG(LogTemp, Error, TEXT("Missing LidarManager class or it already exists."));
188 }
189
191 {
192 // Try to find from the world, if not found, spawn a new one
193 AActor* simulationLevelManagerActor = UGameplayStatics::GetActorOfClass(World, (TSubclassOf<AActor>)SimulationLevelManagerClass);
194 if (IsValid(simulationLevelManagerActor))
195 {
196 SimulationLevelManager = Cast<ASimulationLevelManager>(simulationLevelManagerActor);
197 }
198 else
199 {
201 }
202 }
203
204 if (!Weather)
205 {
206 // Try to find AWeather (BP_Weather) from the level.
207 TArray<AActor*> WeatherActors;
208 UGameplayStatics::GetAllActorsOfClass(World, AWeather::StaticClass(), WeatherActors);
209 if (!WeatherActors.IsEmpty())
210 {
211 Weather = Cast<AWeather>(WeatherActors[0]);
212
213#if WITH_EDITOR
214 if (WeatherActors.Num() >= 2)
215 {
216 UE_LOG(LogTemp, Warning, TEXT("AAgrarsenseGameModeBase.cpp: Level has multiple Weather blueprints. Make sure to only use one."));
217 }
218#endif
219 }
220 }
221
222 if (!InstancedRenderer)
223 {
224 TArray<AActor*> InstancedRendererActors;
225 UGameplayStatics::GetAllActorsOfClass(World, AInstancedRendererManager::StaticClass(), InstancedRendererActors);
226 if (!InstancedRendererActors.IsEmpty())
227 {
228 InstancedRenderer = Cast<AInstancedRendererManager>(InstancedRendererActors[0]);
229 }
230 else
231 {
232 InstancedRenderer = World->SpawnActor<AInstancedRendererManager>();
233 }
234 }
235}
236
238{
239 UWorld* World = GetWorld();
240
241 // Spawn developer tools widget (Ctrl + F9 to open)
242 // this cannot be spawned in InitGame!
243 if (DeveloperTools)
244 {
245 auto developerToolsWidget = CreateWidget<UUserWidget>(World, DeveloperTools);
246 developerToolsWidget->AddToViewport(10);
247 }
248#if WITH_EDITOR
249 else
250 {
251 UE_LOG(LogTemp, Error, TEXT("Missing DeveloperTools class! Please open AgrarsenseGameMode in Unreal and add it."));
252 }
253#endif
254
255 if (!ROSCommands)
256 {
257 ROSCommands = NewObject<UROSCommands>();
258 ROSCommands->Init();
259 }
260
262
263 if (!Spectator)
264 {
265 // try to get Spectator actor
266 TArray<AActor*> Spectators;
267 UGameplayStatics::GetAllActorsOfClass(World, ASpectator::StaticClass(), Spectators);
268 if (!Spectators.IsEmpty())
269 {
270 Spectator = Cast<ASpectator>(Spectators[0]);
271
272#if WITH_EDITOR
273 if (Spectators.Num() >= 2)
274 {
275 UE_LOG(LogTemp, Warning, TEXT("AAgrarsenseGameModeBase.cpp: Level has multiple Spectator blueprints. Make sure to only use one."));
276 }
277#endif
278 }
279 }
280
281 if (!Tagger)
282 {
283 // Destroy all existing Tagger if there's any, there should only be one.
284 TArray<AActor*> Taggers;
285 UGameplayStatics::GetAllActorsOfClass(World, ATagger::StaticClass(), Taggers);
286 for (AActor* TaggerActor : Taggers)
287 {
288 if (TaggerActor)
289 {
290 TaggerActor->Destroy();
291 }
292 }
293 Taggers.Empty();
294 Tagger = World->SpawnActor<ATagger>();
295 }
296
297 if (World && GEngine && GEngine->GameViewport)
298 {
299 // Create and add a drag-and-drop overlay widget to the main viewport (Spectator).
300 // Note: All widgets block the drag-and-drop interaction.
301 // To ensure smooth usage, try dragging the file into an area without any visible widgets.
302 // If the cursor shows a cancel icon, it indicates a hidden widget is obstructing the view.
303 TSharedPtr<SDragAndDropWidget> OverlayWidget = SNew(SDragAndDropWidget);
304 GEngine->GameViewport->AddViewportWidgetContent(OverlayWidget.ToSharedRef());
305 }
306}
AAgrarsenseGameModeBase(const FObjectInitializer &ObjectInitializer)
TSubclassOf< ASimulationLevelManager > SimulationLevelManagerClass
void EndPlay(const EEndPlayReason::Type EndPlayReason) override
ASimulationLevelManager * SimulationLevelManager
AInstancedRendererManager * InstancedRenderer
void InitGame(const FString &MapName, const FString &Options, FString &ErrorMessage) override
TSubclassOf< UUserWidget > DeveloperTools
TSubclassOf< ALidarManager > LidarManagerClass
Definition: Tagger.h:51
static void Log(const FString &Message, bool LogToTextFile=true, bool LogToROS=true)
static void Shutdown()
static void Create()
static FString DataPathForThisRun
void Setup(UWorld *World)
static UAgrarsenseSettings * GetAgrarsenseSettings()
static void Init()
Definition: InfoTopic.cpp:24
static void Destroy()
Definition: InfoTopic.cpp:33
void Destroy()
Definition: ROSCommands.cpp:59
void Setup(bool RosIsConnected)
Definition: ROSHandler.cpp:11
void LaunchROSBridge()
Definition: ROSHandler.cpp:21