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