CARLA
Loading...
Searching...
No Matches
WalkerActorFactory.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
7
14
15#include <util/ue-header-guard-begin.h>
16#include "Json.h"
17#include "JsonUtilities.h"
18#include "Misc/FileHelper.h"
19#include "Misc/Paths.h"
20#include <util/ue-header-guard-end.h>
21
22TArray<FActorDefinition> AWalkerActorFactory::GetDefinitions()
23{
24 // Rebuilt from scratch every episode, otherwise entries accumulate.
25 Definitions.Reset();
26 WalkersParams.Reset();
27 LoadWalkerParametersArrayFromFile("WalkerParameters.json", WalkersParams);
28 // Content packs: <Pack>/Content/Config/WalkerParameters.json, later packs
29 // override earlier entries with the same Id.
30 for (const FString &PackFile : UCarlaContentPackManager::FindPackCatalogFiles("WalkerParameters.json"))
31 {
32 TArray<FPedestrianParameters> PackWalkersParams;
33 LoadWalkerParametersArrayFromFile(PackFile, PackWalkersParams);
35 [](const FPedestrianParameters &W) { return W.Id; });
36 }
37
39 return Definitions;
40}
41
43 const FTransform &SpawnAtTransform,
44 const FActorDescription &ActorDescription)
45{
46 FActorSpawnResult SpawnResult;
47 if(!IsValid(ActorDescription.Class))
48 {
49 UE_LOG(LogCarla, Error, TEXT("Actor Description Class is null."));
50 SpawnResult.Status = EActorSpawnResultStatus::InvalidDescription;
51 return SpawnResult;
52 }
53
54 AActor* SpawnedActor = GetWorld()->SpawnActor<AActor>(ActorDescription.Class, SpawnAtTransform);
55 SpawnResult.Actor = SpawnedActor;
56
57 if(SpawnedActor == nullptr)
58 {
59 SpawnResult.Status = EActorSpawnResultStatus::Collision;
60 return SpawnResult;
61 }
62
63 // Wire the "use_wheelchair" user attribute through to the walker before
64 // PostProcessWalker runs, so the blueprint attach logic can read it.
65 AWalkerBase* Walker = Cast<AWalkerBase>(SpawnedActor);
66 if(Walker)
67 {
69 TEXT("use_wheelchair"), ActorDescription.Variations, false);
70 }
71
72 if(PostProcessWalker(SpawnedActor, ActorDescription))
73 {
74 // After PostProcessWalker so the blueprint's material/mesh post-
75 // processing cannot stomp the wheelchair animation override.
76 if(Walker && Walker->bUsesWheelChair)
77 {
78 Walker->AttachWheelchair();
79 }
80 SpawnResult.Status = EActorSpawnResultStatus::Success;
81 return SpawnResult;
82 }
83
84 SpawnResult.Status = EActorSpawnResultStatus::UnknownError;
85 return SpawnResult;
86}
87
88void AWalkerActorFactory::ReleaseContentPack(const FString &MountPoint)
89{
90 Definitions.RemoveAll([&](const FActorDefinition &D) { return DefinitionReferencesPath(D, MountPoint); });
91 WalkersParams.RemoveAll([&](const FPedestrianParameters &W) {
92 return W.Class.ToSoftObjectPath().ToString().StartsWith(MountPoint);
93 });
94}
95
97{
98 TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
99 JsonObject->SetStringField(TEXT("Id"), WalkerParams.Id);
100 JsonObject->SetStringField(TEXT("Class"), WalkerParams.Class.ToSoftObjectPath().ToString());
101
102 FString GenderString;
103 switch(WalkerParams.Gender)
104 {
105 case EPedestrianGender::Other:
106 GenderString = FString("Other");
107 break;
108 case EPedestrianGender::Female:
109 GenderString = FString("Female");
110 break;
111 case EPedestrianGender::Male:
112 GenderString = FString("Male");
113 break;
114 default:
115 GenderString = FString("INVALID");
116 break;
117 }
118 JsonObject->SetStringField(TEXT("Gender"), GenderString);
119
120 FString AgeString;
121 switch(WalkerParams.Age)
122 {
123 case EPedestrianAge::Child:
124 AgeString = FString("Child");
125 break;
126 case EPedestrianAge::Teenager:
127 AgeString = FString("Teenager");
128 break;
129 case EPedestrianAge::Adult:
130 AgeString = FString("Adult");
131 break;
132 case EPedestrianAge::Elderly:
133 AgeString = FString("Elderly");
134 break;
135 default:
136 AgeString = FString("INVALID");
137 break;
138 }
139 JsonObject->SetStringField(TEXT("Age"), AgeString);
140
141 //JsonObject->SetNumberField(TEXT("Speed"), InSpeed);
142 TArray<TSharedPtr<FJsonValue>> SpeedArray;
143 for (const float& Speed : WalkerParams.Speed)
144 {
145 TSharedPtr<FJsonObject> SpeedObject = MakeShareable(new FJsonObject);
146 SpeedObject->SetNumberField(TEXT("Speed"), Speed);
147 SpeedArray.Add(MakeShareable(new FJsonValueObject(SpeedObject)));
148 }
149 JsonObject->SetArrayField(TEXT("Speeds"), SpeedArray);
150
151 JsonObject->SetNumberField(TEXT("Generation"), WalkerParams.Generation);
152
153 JsonObject->SetStringField(TEXT("SpecialType"), WalkerParams.SpecialType);
154
155 JsonObject->SetBoolField(TEXT("CanUseWheelChair"), WalkerParams.bCanUseWheelChair);
156
157 return JsonObject;
158}
159
160FString AWalkerActorFactory::FWalkerParametersArrayToJson(const TArray<FPedestrianParameters>& WalkerParamsArray)
161{
162 TArray<TSharedPtr<FJsonValue>> JsonArray;
163
164 for (const FPedestrianParameters& WalkerParams : WalkerParamsArray)
165 {
166 // Convert each FPedestrianParameters to a JSON object
167 TSharedPtr<FJsonObject> JsonObject = FWalkerParametersToJsonObject(WalkerParams);
168 JsonArray.Add(MakeShareable(new FJsonValueObject(JsonObject)));
169 }
170
171 // Convert the array of JSON objects into a single JSON object
172 TSharedRef<FJsonObject> RootObject = MakeShareable(new FJsonObject);
173 RootObject->SetArrayField(TEXT("Walkers"), JsonArray);
174
175 // Serialize the JSON object into an FString
176 FString OutputString;
177 TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString);
178 FJsonSerializer::Serialize(RootObject, Writer);
179
180 return OutputString;
181}
182
183void AWalkerActorFactory::SaveWalkerParametersArrayToFile(const TArray<FPedestrianParameters>& WalkerParametersArray, const FString& FileName)
184{
185 FString FilePath = FPaths::ProjectContentDir() + TEXT("Carla/Config/") + FileName;
186 // Convert the array to an FString in JSON format
187 FString JsonContent = FWalkerParametersArrayToJson(WalkerParametersArray);
188
189 // Save the JSON to a file
190 if (FFileHelper::SaveStringToFile(JsonContent, *FilePath))
191 {
192 UE_LOG(LogCarla, Log, TEXT("JSON file successfully saved at: %s"), *FilePath);
193 }
194 else
195 {
196 UE_LOG(LogCarla, Error, TEXT("Failed to save JSON file at: %s"), *FilePath);
197 }
198}
199
200bool AWalkerActorFactory::JsonToFWalkerParameters(const TSharedPtr<FJsonObject> JsonObject, FPedestrianParameters& OutWalkerParams)
201{
202 if (!JsonObject.IsValid()) return false;
203
204 JsonObject->TryGetStringField(TEXT("Id"), OutWalkerParams.Id);
205
206 // Build the soft class reference from the path string; the actual UClass
207 // load is deferred to MakePedestrianDefinition so the JSON parse itself
208 // does not block on disk I/O.
209 FString ClassPath;
210 JsonObject->TryGetStringField(TEXT("Class"), ClassPath);
211 OutWalkerParams.Class = TSoftClassPtr<ACharacter>(FSoftObjectPath(ClassPath));
212
213 FString GenderString;
214 JsonObject->TryGetStringField(TEXT("Gender"), GenderString);
215
216 if(GenderString == FString("Other")){
217 OutWalkerParams.Gender = EPedestrianGender::Other;
218 }
219 else if(GenderString == FString("Female"))
220 {
221 OutWalkerParams.Gender = EPedestrianGender::Female;
222 }
223 else if(GenderString == FString("Male"))
224 {
225 OutWalkerParams.Gender = EPedestrianGender::Male;
226 }
227 else
228 {
229 OutWalkerParams.Gender = EPedestrianGender::INVALID;
230 }
231
232 FString AgeString;
233 JsonObject->TryGetStringField(TEXT("Age"), AgeString);
234
235 if(AgeString == FString("Child")){
236 OutWalkerParams.Age = EPedestrianAge::Child;
237 }
238 else if(AgeString == FString("Teenager"))
239 {
240 OutWalkerParams.Age = EPedestrianAge::Teenager;
241 }
242 else if(AgeString == FString("Adult"))
243 {
244 OutWalkerParams.Age = EPedestrianAge::Adult;
245 }
246 else if(AgeString == FString("Elderly"))
247 {
248 OutWalkerParams.Age = EPedestrianAge::Elderly;
249 }
250 else
251 {
252 OutWalkerParams.Age = EPedestrianAge::INVALID;
253 }
254
255 // Parse Speeds
256 const TArray<TSharedPtr<FJsonValue>>* SpeedsArray;
257 if (JsonObject->TryGetArrayField(TEXT("Speeds"), SpeedsArray))
258 {
259 OutWalkerParams.Speed.Empty();
260 for (const TSharedPtr<FJsonValue>& SpeedValue : *SpeedsArray)
261 {
262 TSharedPtr<FJsonObject> SpeedObject = SpeedValue->AsObject();
263 if (SpeedObject.IsValid())
264 {
265 float Speed;
266 Speed = SpeedObject->GetNumberField(TEXT("Speed"));
267 OutWalkerParams.Speed.Add(Speed);
268 }
269 }
270 }
271
272 JsonObject->TryGetNumberField(TEXT("Generation"), OutWalkerParams.Generation);
273
274 // Optional field: the pre-wheelchair catalogs do not carry it, keep the
275 // struct default (false) when absent.
276 bool bCanUseWheelChair = false;
277 if (JsonObject->TryGetBoolField(TEXT("CanUseWheelChair"), bCanUseWheelChair))
278 {
279 OutWalkerParams.bCanUseWheelChair = bCanUseWheelChair;
280 }
281
282 JsonObject->TryGetStringField(TEXT("SpecialType"), OutWalkerParams.SpecialType);
283
284 return true;
285}
286
287bool AWalkerActorFactory::JsonToFWalkerParametersArray(const FString& JsonString, TArray<FPedestrianParameters>& OutWalkerParamsArray)
288{
289 TSharedPtr<FJsonObject> RootObject;
290 TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);
291
292 if (!FJsonSerializer::Deserialize(Reader, RootObject) || !RootObject.IsValid())
293 {
294 UE_LOG(LogCarla, Error, TEXT("Failed to parse JSON."));
295 return false;
296 }
297
298 // Get the "Walkers" array from the JSON root object
299 const TArray<TSharedPtr<FJsonValue>>* WalkersArray;
300 if (RootObject->TryGetArrayField(TEXT("Walkers"), WalkersArray))
301 {
302 OutWalkerParamsArray.Empty();
303 for (const TSharedPtr<FJsonValue>& WalkerValue : *WalkersArray)
304 {
305 TSharedPtr<FJsonObject> WalkerObject = WalkerValue->AsObject();
306 if (WalkerObject.IsValid())
307 {
308 FPedestrianParameters WalkerParams;
309 if (JsonToFWalkerParameters(WalkerObject, WalkerParams))
310 {
311 OutWalkerParamsArray.Add(WalkerParams);
312 }
313 }
314 }
315 }
316
317 return true;
318}
319
320void AWalkerActorFactory::LoadWalkerParametersArrayFromFile(const FString& FileName, TArray<FPedestrianParameters>& OutWalkerParamsArray)
321{
322 FString JsonString;
323 // A relative name is resolved against the project catalog dir; an absolute
324 // path (content pack catalogs) is used as is.
325 FString FilePath = FPaths::IsRelative(FileName)
326 ? FPaths::ProjectContentDir() + TEXT("Carla/Config/") + FileName
327 : FileName;
328 FString JsonContent;
329
330 // Load the JSON file content into an FString
331 if (FFileHelper::LoadFileToString(JsonContent, *FilePath))
332 {
333 // Parse the JSON and populate the TArray<FPedestrianParameters>
334 if (JsonToFWalkerParametersArray(JsonContent, OutWalkerParamsArray))
335 {
336 UE_LOG(LogCarla, Log, TEXT("Walker parameters loaded successfully from %s"), *FilePath);
337 }
338 else
339 {
340 UE_LOG(LogCarla, Error, TEXT("Failed to parse Walker parameters from %s"), *FilePath);
341 }
342 }
343 else
344 {
345 UE_LOG(LogCarla, Error, TEXT("Failed to load file: %s"), *FilePath);
346 }
347}
static bool IsValid(const ACarlaWheeledVehicle *Vehicle)
static bool DefinitionReferencesPath(const FActorDefinition &Definition, const FString &MountPoint)
True when the definition's class or any attribute/variation value points at content under MountPoint.
static FString FWalkerParametersArrayToJson(const TArray< FPedestrianParameters > &WalkerParamsArray)
TArray< FPedestrianParameters > WalkersParams
static TSharedPtr< FJsonObject > FWalkerParametersToJsonObject(const FPedestrianParameters &WalkerParams)
static void LoadWalkerParametersArrayFromFile(const FString &FileName, TArray< FPedestrianParameters > &OutWalkerParamsArray)
virtual TArray< FActorDefinition > GetDefinitions() override
Retrieve the definitions of the static mesh actor.
virtual FActorSpawnResult SpawnActor(const FTransform &SpawnAtTransform, const FActorDescription &ActorDescription) override
Spawn an actor based on ActorDescription and Transform.
bool PostProcessWalker(AActor *SpawnedActor, const FActorDescription &WalkerParams)
TArray< FActorDefinition > Definitions
static void SaveWalkerParametersArrayToFile(const TArray< FPedestrianParameters > &WalkerParamsArray, const FString &FileName)
static bool JsonToFWalkerParametersArray(const FString &JsonString, TArray< FPedestrianParameters > &OutWalkerParamsArray)
virtual void ReleaseContentPack(const FString &MountPoint) override
Drop every cached definition, parameter and loaded asset that refers to content under MountPoint ("/<...
static bool JsonToFWalkerParameters(const TSharedPtr< FJsonObject > JsonObject, FPedestrianParameters &OutWalkerParams)
void AttachWheelchair()
Attach the wheelchair prop to the walker and switch the walker to the sitting animation.
bool bUsesWheelChair
Whether this walker was spawned riding a wheelchair (the "use_wheelchair" user attribute).
Definition WalkerBase.h:38
static void MakePedestrianDefinitions(const TArray< FPedestrianParameters > &ParameterArray, TArray< FActorDefinition > &Definitions)
static bool RetrieveActorAttributeToBool(const FString &Id, const TMap< FString, FActorAttribute > &Attributes, bool Default)
static void MergeCatalog(TArray< T > &Into, const TArray< T > &From, KeyFn Key)
Merge catalog entries: an entry whose key already exists replaces it in place (later files override b...
static TArray< FString > FindPackCatalogFiles(const FString &RelativeName)
<ContentDir>/Config/<RelativeName> of every mounted pack where the file exists, in mount order (later...
A definition of a Carla Actor with all the variation and attributes.
A description of a Carla Actor with all its variation.
TMap< FString, FActorAttribute > Variations
User selected variations of the actor.
TSubclassOf< AActor > Class
Class of the actor to be spawned.
Result of an actor spawn function.
EActorSpawnResultStatus Status
TSoftClassPtr< ACharacter > Class