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
25#include "ROSIntegration/Classes/ROSIntegrationGameInstance.h"
26
30
31AAgrarsenseGameModeBase::AAgrarsenseGameModeBase(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
32{
33 PrimaryActorTick.bCanEverTick = false;
34}
35
36void AAgrarsenseGameModeBase::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
37{
38 Super::InitGame(MapName, Options, ErrorMessage);
39
41}
42
44{
45 Super::BeginPlay();
46
48
50}
51
52void AAgrarsenseGameModeBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
53{
54 Super::EndPlay(EndPlayReason);
55
56 if (EndPlayReason == EEndPlayReason::Type::Quit || EndPlayReason == EEndPlayReason::Type::EndPlayInEditor)
57 {
59
60 if (ROSCommands)
61 {
63 }
64
65 SimulatorLog::Log("Simulator shutting down. Goodbye.");
67 }
68
69#if WITH_EDITOR
70 if (EndPlayReason == EEndPlayReason::Type::EndPlayInEditor)
71 {
72 EditorEndPlayCleanup();
73 }
74#endif
75}
76
77#if WITH_EDITOR
78void AAgrarsenseGameModeBase::EditorEndPlayCleanup()
79{
80 // TODO: this is not good approach.
81 // Clear certain static variables at Unreal Editor EndPlay.
82 // This is not relevant for packaged builds as most of this data
83 // is meant to be kept in memory as long as the Simulator is running.
84 IActorInformation::ClearTMapEditor();
87 StartMessageLogged = false;
88 MapChanged = false;
89}
90#endif
91
93{
94 if (MapChanged)
95 {
96 return;
97 }
98
99 FString LevelName;
100 if (FParse::Param(FCommandLine::Get(), TEXT("-vindeln")) && !MapChanged)
101 {
102 LevelName = "/Game/Agrarsense/Maps/Vindeln/Vindeln";
103 }
104 else if (FParse::Param(FCommandLine::Get(), TEXT("-rovaniemi")) && !MapChanged)
105 {
106 LevelName = "/Game/Agrarsense/Maps/RovaniemiForest/RovaniemiForest";
107 }
108 else if (FParse::Param(FCommandLine::Get(), TEXT("-playground")) && !MapChanged)
109 {
110 LevelName = "/Game/Agrarsense/Maps/Playground";
111 }
112
113 if (!LevelName.IsEmpty())
114 {
115 MapChanged = true;
116 UGameplayStatics::OpenLevel(GetWorld(), *LevelName);
117 }
118}
119
121{
123 {
125 SimulatorLog::Log("Simulator starting.");
126 StartMessageLogged = true;
127 }
128
129 UWorld* World = GetWorld();
130
131 // Setup Agrarsense settings
133 if (Settings)
134 {
135 Settings->Setup(World);
136 }
137
138 UROSIntegrationGameInstance* RosInstance = Cast<UROSIntegrationGameInstance>(World->GetGameInstance());
139 bool ROSConnected = RosInstance->IsROSConnected();
140
141 // Create UROSHandle UObject
142 if (!ROSHandle)
143 {
144 ROSHandle = NewObject<UROSHandler>();
145
146 if (ROSHandle)
147 {
148 ROSHandle->Setup(ROSConnected);
149 if (RosInstance->bConnectToROS && !ROSConnected && AttemptToLaunchROSBridge)
150 {
152
153 // Only try to attempt once.
154 // Otherwise if ROS is not connected, simulator attempts to launch ROS Bridge
155 // process each time map is changed.
157 }
158 }
159 }
160
161 // Spawn Lidar Mananger. This actor should always exists in the scene.
162 // This actor only ticks when there is Lidar sensor(s) in the world.
164 {
165 LidarManager = World->SpawnActor<ALidarManager>(LidarManagerClass);
166 }
167 else
168 {
169 UE_LOG(LogTemp, Error, TEXT("Missing LidarManager class or it already exists."));
170 }
171
173 {
174 // Try to find from the world, if not found, spawn a new one
175 AActor* simulationLevelManagerActor = UGameplayStatics::GetActorOfClass(World, (TSubclassOf<AActor>)SimulationLevelManagerClass);
176 if (IsValid(simulationLevelManagerActor))
177 {
178 SimulationLevelManager = Cast<ASimulationLevelManager>(simulationLevelManagerActor);
179 }
180 else
181 {
183 }
184 }
185
186 if (!Weather)
187 {
188 // Try to find AWeather (BP_Weather) from the level.
189 TArray<AActor*> WeatherActors;
190 UGameplayStatics::GetAllActorsOfClass(World, AWeather::StaticClass(), WeatherActors);
191 if (!WeatherActors.IsEmpty())
192 {
193 Weather = Cast<AWeather>(WeatherActors[0]);
194
195#if WITH_EDITOR
196 if (WeatherActors.Num() >= 2)
197 {
198 UE_LOG(LogTemp, Warning, TEXT("AAgrarsenseGameModeBase.cpp: Level has multiple Weather blueprints. Make sure to only use one."));
199 }
200#endif
201 }
202 }
203
204 if (!InstancedRenderer)
205 {
206 TArray<AActor*> InstancedRendererActors;
207 UGameplayStatics::GetAllActorsOfClass(World, AInstancedRenderer::StaticClass(), InstancedRendererActors);
208 if (!InstancedRendererActors.IsEmpty())
209 {
210 InstancedRenderer = Cast<AInstancedRenderer>(InstancedRendererActors[0]);
211 }
212 else
213 {
214 InstancedRenderer = World->SpawnActor<AInstancedRenderer>();
215 }
216 }
217}
218
220{
221 UWorld* World = GetWorld();
222
223 // Spawn developer tools widget (Ctrl + F9 to open)
224 // this cannot be spawned in InitGame!
225 if (DeveloperTools)
226 {
227 auto developerToolsWidget = CreateWidget<UUserWidget>(World, DeveloperTools);
228 developerToolsWidget->AddToViewport(10);
229 }
230#if WITH_EDITOR
231 else
232 {
233 UE_LOG(LogTemp, Error, TEXT("Missing DeveloperTools class! Please open AgrarsenseGameMode in Unreal and add it."));
234 }
235#endif
236
237 if (!ROSCommands)
238 {
239 ROSCommands = NewObject<UROSCommands>();
240 ROSCommands->Init();
241 }
242
244
245 if (!Spectator)
246 {
247 // try to get Spectator actor
248 TArray<AActor*> Spectators;
249 UGameplayStatics::GetAllActorsOfClass(World, ASpectator::StaticClass(), Spectators);
250 if (!Spectators.IsEmpty())
251 {
252 Spectator = Cast<ASpectator>(Spectators[0]);
253
254#if WITH_EDITOR
255 if (Spectators.Num() >= 2)
256 {
257 UE_LOG(LogTemp, Warning, TEXT("AAgrarsenseGameModeBase.cpp: Level has multiple Spectator blueprints. Make sure to only use one."));
258 }
259#endif
260 }
261 }
262
263 if (!Tagger)
264 {
265 // Destroy all existing Tagger if there's any, there should only be one.
266 TArray<AActor*> Taggers;
267 UGameplayStatics::GetAllActorsOfClass(World, ATagger::StaticClass(), Taggers);
268 for (AActor* TaggerActor : Taggers)
269 {
270 if (TaggerActor)
271 {
272 TaggerActor->Destroy();
273 }
274 }
275 Taggers.Empty();
276 Tagger = World->SpawnActor<ATagger>();
277 }
278
279 if (World && GEngine && GEngine->GameViewport)
280 {
281 // Create and add a drag-and-drop overlay widget to the main viewport (Spectator).
282 // Note: All widgets block the drag-and-drop interaction.
283 // To ensure smooth usage, try dragging the file into an area without any visible widgets.
284 // If the cursor shows a cancel icon, it indicates a hidden widget is obstructing the view.
285 TSharedPtr<SDragAndDropWidget> OverlayWidget = SNew(SDragAndDropWidget);
286 GEngine->GameViewport->AddViewportWidgetContent(OverlayWidget.ToSharedRef());
287 }
288}
AAgrarsenseGameModeBase(const FObjectInitializer &ObjectInitializer)
TSubclassOf< ASimulationLevelManager > SimulationLevelManagerClass
void EndPlay(const EEndPlayReason::Type EndPlayReason) override
ASimulationLevelManager * SimulationLevelManager
AInstancedRenderer * InstancedRenderer
void InitGame(const FString &MapName, const FString &Options, FString &ErrorMessage) override
TSubclassOf< UUserWidget > DeveloperTools
TSubclassOf< ALidarManager > LidarManagerClass
Definition: Tagger.h:29
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