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"
22
23#include "Kismet/GameplayStatics.h"
24#include "Kismet/KismetSystemLibrary.h"
25#include "Engine/World.h"
26
27#include "ROSIntegration/Classes/ROSIntegrationGameInstance.h"
28
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();
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 // Create AgrarsenseConsoleCommands UObject
132 if (AgrarsenseCommands == nullptr)
133 {
134 AgrarsenseCommands = NewObject<UAgrarsenseConsoleCommands>();
135 }
136
137 // Setup Agrarsense settings
139 if (Settings)
140 {
141 Settings->Setup(World);
142 }
143
144 UROSIntegrationGameInstance* RosInstance = Cast<UROSIntegrationGameInstance>(World->GetGameInstance());
145 bool ROSConnected = RosInstance->IsROSConnected();
146
147 // Create UROSHandle UObject
148 if (ROSHandle == nullptr)
149 {
150 ROSHandle = NewObject<UROSHandler>();
151 ROSHandle->Setup(ROSConnected);
152 }
153
154 if (ROSHandle && RosInstance->bConnectToROS && !ROSConnected)
155 {
157 }
158
159 // Spawn Lidar Mananger. This actor should always exists in the scene.
160 // This actor only ticks when there is Lidar sensor(s) in the world.
161 if (LidarManagerClass != nullptr)
162 {
163 LidarManager = World->SpawnActor<ALidarManager>(LidarManagerClass);
164 }
165 else
166 {
167 UE_LOG(LogTemp, Error, TEXT("Missing LidarManager class! Please open AgrarsenseGameMode in Unreal and add it."));
168 }
169
170 if (SimulationLevelManager == nullptr)
171 {
172 // Try to find from the world, if not found, spawn a new one
173 AActor* simulationLevelManagerActor = UGameplayStatics::GetActorOfClass(World, (TSubclassOf<AActor>)SimulationLevelManagerClass);
174 if (IsValid(simulationLevelManagerActor))
175 {
176 SimulationLevelManager = Cast<ASimulationLevelManager>(simulationLevelManagerActor);
177 }
178 else
179 {
181 }
182 }
183
184 // Try to find AWeather (BP_Weather) from the level.
185 TArray<AActor*> WeatherActors;
186 UGameplayStatics::GetAllActorsOfClass(World, AWeather::StaticClass(), WeatherActors);
187 if (!WeatherActors.IsEmpty())
188 {
189 Weather = Cast<AWeather>(WeatherActors[0]);
190
191#if WITH_EDITOR
192 if (WeatherActors.Num() >= 2)
193 {
194 UE_LOG(LogTemp, Warning, TEXT("AAgrarsenseGameModeBase.cpp: Level has multiple Weather blueprints. Make sure to only use one."));
195 }
196#endif
197 }
198
199 // Spawn Instanced Renderer to the world.
200 if (InstancedRenderer == nullptr)
201 {
202 InstancedRenderer = World->SpawnActor<AInstancedRenderer>();
203 }
204}
205
207{
208 UWorld* World = GetWorld();
209
210 // Spawn developer tools widget (Ctrl + F9 to open)
211 // this cannot be spawned in InitGame!
212 if (DeveloperTools != nullptr)
213 {
214 auto developerToolsWidget = CreateWidget<UUserWidget>(World, DeveloperTools);
215 developerToolsWidget->AddToViewport(10);
216 }
217#if WITH_EDITOR
218 else
219 {
220 UE_LOG(LogTemp, Error, TEXT("Missing DeveloperTools class! Please open AgrarsenseGameMode in Unreal and add it."));
221 }
222#endif
223
224 if (ROSCommands == nullptr)
225 {
226 ROSCommands = NewObject<UROSCommands>();
227 ROSCommands->Init();
228 }
229
231
232 // try to get Spectator actor
233 TArray<AActor*> Spectators;
234 UGameplayStatics::GetAllActorsOfClass(World, ASpectator::StaticClass(), Spectators);
235 if (!Spectators.IsEmpty())
236 {
237 Spectator = Cast<ASpectator>(Spectators[0]);
238
239#if WITH_EDITOR
240 if (Spectators.Num() >= 2)
241 {
242 UE_LOG(LogTemp, Warning, TEXT("AAgrarsenseGameModeBase.cpp: Level has multiple Spectator blueprints. Make sure to only use one."));
243 }
244#endif
245 }
246
247 // Destroy all existing Tagger if there's any, there should only be one.
248 TArray<AActor*> Taggers;
249 UGameplayStatics::GetAllActorsOfClass(World, ATagger::StaticClass(), Taggers);
250 for (AActor* TaggerActor : Taggers)
251 {
252 if (TaggerActor)
253 {
254 TaggerActor->Destroy();
255 }
256 }
257 Taggers.Empty();
258
259 Tagger = World->SpawnActor<ATagger>();
260
261
262 if (World && GEngine && GEngine->GameViewport)
263 {
264 // Create and add a drag-and-drop overlay widget to the main viewport (Spectator).
265 // Note: All widgets block the drag-and-drop interaction.
266 // To ensure smooth usage, try dragging the file into an area without any visible widgets.
267 // If the cursor shows a cancel icon, it indicates a hidden widget is obstructing the view.
268 TSharedPtr<SDragAndDropWidget> OverlayWidget = SNew(SDragAndDropWidget);
269 GEngine->GameViewport->AddViewportWidgetContent(OverlayWidget.ToSharedRef());
270 }
271}
AAgrarsenseGameModeBase(const FObjectInitializer &ObjectInitializer)
TSubclassOf< ASimulationLevelManager > SimulationLevelManagerClass
void EndPlay(const EEndPlayReason::Type EndPlayReason) override
UAgrarsenseConsoleCommands * AgrarsenseCommands
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