From 8ae2770e1a1afaa2d8f65ecd059965487c3aa1b5 Mon Sep 17 00:00:00 2001 From: nsubiron Date: Wed, 25 Jul 2018 11:47:57 +0200 Subject: [PATCH] Improvements to actor spawners --- LibCarla/source/carla/client/Client.cpp | 2 +- PythonAPI/source/libcarla/Exception.cpp | 9 ++-- .../Carla/Source/Carla/Actor/ActorAttribute.h | 4 +- .../Source/Carla/Actor/ActorDefinition.h | 7 +++ .../Source/Carla/Actor/ActorDescription.h | 4 ++ .../Source/Carla/Actor/ActorDispatcher.cpp | 24 ++++++++- .../Source/Carla/Actor/ActorDispatcher.h | 6 ++- .../Source/Carla/Actor/ActorSpawnResult.cpp | 2 +- .../Source/Carla/Actor/ActorSpawnResult.h | 8 +++ .../Carla/Source/Carla/Actor/ActorSpawner.h | 25 +++++++-- ...lueprintBase.h => ActorSpawnerBlueprint.h} | 16 +++--- .../Carla/Source/Carla/Game/CarlaEpisode.h | 2 +- .../Carla/Game/TheNewCarlaGameModeBase.cpp | 6 +-- .../Carla/Game/TheNewCarlaGameModeBase.h | 8 +-- .../Source/Carla/Server/TheNewCarlaServer.cpp | 54 ++++++++++++++++--- 15 files changed, 139 insertions(+), 38 deletions(-) rename Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/{ActorSpawnerBlueprintBase.h => ActorSpawnerBlueprint.h} (75%) diff --git a/LibCarla/source/carla/client/Client.cpp b/LibCarla/source/carla/client/Client.cpp index 39fd5855e..81b3d5b30 100644 --- a/LibCarla/source/carla/client/Client.cpp +++ b/LibCarla/source/carla/client/Client.cpp @@ -23,7 +23,7 @@ namespace client { SharedPtr Client::SpawnActor( const ActorBlueprint &blueprint, const Transform &transform) { - auto actor = Call("spawn_actor", blueprint.MakeActorDescription(), transform); + auto actor = Call("spawn_actor", transform, blueprint.MakeActorDescription()); return SharedPtr(new Actor{actor, GetWorld()}); } diff --git a/PythonAPI/source/libcarla/Exception.cpp b/PythonAPI/source/libcarla/Exception.cpp index be6438081..e9ceffeee 100644 --- a/PythonAPI/source/libcarla/Exception.cpp +++ b/PythonAPI/source/libcarla/Exception.cpp @@ -10,11 +10,12 @@ #include -void translator(rpc::rpc_error e) { +void translator(const rpc::rpc_error &e) { std::stringstream ss; - ss << e.what() - << " in function " << e.get_function_name() - << ": " << e.get_error().as(); + ss << e.what() << " in function " << e.get_function_name(); + /// @todo Supposedly we can extract the error string here as provided by the + /// server with e.get_error().as(), but it gives the wrong + /// string. PyErr_SetString(PyExc_RuntimeError, ss.str().c_str()); } diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorAttribute.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorAttribute.h index 0ee30a383..985ea1460 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorAttribute.h +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorAttribute.h @@ -43,7 +43,7 @@ struct CARLA_API FActorVariation FString Id; UPROPERTY(EditAnywhere, BlueprintReadWrite) - EActorAttributeType Type = EActorAttributeType::Int; + EActorAttributeType Type = EActorAttributeType::String; UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray RecommendedValues; @@ -63,7 +63,7 @@ struct CARLA_API FActorAttribute FString Id; UPROPERTY(EditAnywhere, BlueprintReadWrite) - EActorAttributeType Type = EActorAttributeType::Int; + EActorAttributeType Type = EActorAttributeType::String; UPROPERTY(EditAnywhere, BlueprintReadWrite) FString Value; diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDefinition.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDefinition.h index a91389808..4e2c5bd45 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDefinition.h +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDefinition.h @@ -25,6 +25,13 @@ struct FActorDefinition UPROPERTY(EditAnywhere, BlueprintReadWrite) FString Id; + /// Class of the actor to be spawned (Optional). + /// + /// Note that this parameter is not exposed on the client-side, only used by + /// the spawner itself. + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSubclassOf Class; + /// A list of comma-separated tags. UPROPERTY(EditAnywhere, BlueprintReadWrite) FString Tags; diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDescription.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDescription.h index bf95b040b..927af8a17 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDescription.h +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDescription.h @@ -23,6 +23,10 @@ struct FActorDescription UPROPERTY(EditAnywhere, BlueprintReadWrite) FString Id; + /// Class of the actor to be spawned. + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TSubclassOf Class; + /// User selected variations of the actor. Note that at this point are /// represented by non-modifiable attributes. UPROPERTY(EditAnywhere, BlueprintReadWrite) diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDispatcher.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDispatcher.cpp index b09246f6a..db6d1b7f9 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDispatcher.cpp +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDispatcher.cpp @@ -17,6 +17,7 @@ void FActorDispatcher::Bind(FActorDefinition Definition, SpawnFunctionType Funct Definition.UId = static_cast(SpawnFunctions.Num()) + 1u; Definitions.Emplace(Definition); SpawnFunctions.Emplace(Functor); + Classes.Emplace(Definition.Class); } else { @@ -24,7 +25,7 @@ void FActorDispatcher::Bind(FActorDefinition Definition, SpawnFunctionType Funct } } -void FActorDispatcher::Bind(IActorSpawner &ActorSpawner) +void FActorDispatcher::Bind(AActorSpawner &ActorSpawner) { for (const auto &Definition : ActorSpawner.MakeDefinitions()) { @@ -40,11 +41,30 @@ TPair FActorDispatcher::SpawnActor( { if ((Description.UId == 0u) || (Description.UId > static_cast(SpawnFunctions.Num()))) { - UE_LOG(LogCarla, Error, TEXT("Invalid ActorDescription \"%s\" (UId=%d)"), *Description.Id, Description.UId); + UE_LOG(LogCarla, Error, TEXT("Invalid ActorDescription '%s' (UId=%d)"), *Description.Id, Description.UId); return MakeTuple(EActorSpawnResultStatus::InvalidDescription, FActorView()); } + + UE_LOG(LogCarla, Log, TEXT("Spawning actor '%s'"), *Description.Id); + + Description.Class = Classes[Description.UId - 1]; auto Result = SpawnFunctions[Description.UId - 1](Transform, Description); + + if ((Result.Status == EActorSpawnResultStatus::Success) && (Result.Actor == nullptr)) + { + UE_LOG(LogCarla, Warning, TEXT("ActorSpawnResult: Trying to spawn '%s'"), *Description.Id); + UE_LOG(LogCarla, Warning, TEXT("ActorSpawnResult: Reported success but did not return an actor")); + Result.Status = EActorSpawnResultStatus::UnknownError; + } + auto View = Result.IsValid() ? Registry.Register(*Result.Actor, std::move(Description)) : FActorView(); + + if (!View.IsValid()) + { + UE_LOG(LogCarla, Warning, TEXT("Failed to spawn actor '%s'"), *Description.Id); + check(Result.Status != EActorSpawnResultStatus::Success); + } + return MakeTuple(Result.Status, View); } diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDispatcher.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDispatcher.h index 3db97b815..f679fecc2 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDispatcher.h +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorDispatcher.h @@ -14,7 +14,7 @@ #include "Containers/Array.h" #include "Templates/Function.h" -class IActorSpawner; +class AActorSpawner; /// Actor in charge of binding ActorDefinitions to spawn functions, as well as /// keeping the registry of all the actors spawned. @@ -33,7 +33,7 @@ public: /// Bind all the definitions of @a ActorSpawner to its spawn function. /// /// @warning Invalid definitions are ignored. - void Bind(IActorSpawner &ActorSpawner); + void Bind(AActorSpawner &ActorSpawner); /// Spawns an actor based on @a ActorDescription at @a Transform. To properly /// despawn an actor created with this function call DestroyActor. @@ -64,5 +64,7 @@ private: TArray SpawnFunctions; + TArray> Classes; + FActorRegistry Registry; }; diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawnResult.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawnResult.cpp index a517cb1b9..13f844989 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawnResult.cpp +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawnResult.cpp @@ -22,7 +22,7 @@ FString FActorSpawnResult::StatusToString(EActorSpawnResultStatus InStatus) case EActorSpawnResultStatus::InvalidDescription: return TEXT("Spawn failed because of invalid actor description"); case EActorSpawnResultStatus::Collision: - return TEXT("Spawn failed because due to a collision at spawn position"); + return TEXT("Spawn failed because of collision at spawn position"); case EActorSpawnResultStatus::UnknownError: default: return TEXT("Unknown error while trying to spawn actor"); diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawnResult.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawnResult.h index a8fb4da20..a7c3e93c8 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawnResult.h +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawnResult.h @@ -26,6 +26,14 @@ struct FActorSpawnResult { GENERATED_BODY() + FActorSpawnResult() = default; + + explicit FActorSpawnResult(AActor *InActor) + : Actor(InActor), + Status(Actor != nullptr ? + EActorSpawnResultStatus::Success : + EActorSpawnResultStatus::UnknownError) {} + static FString StatusToString(EActorSpawnResultStatus Status); bool IsValid() const diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawner.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawner.h index fc6475408..ec3e61082 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawner.h +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawner.h @@ -13,15 +13,27 @@ #include "Containers/Array.h" #include "GameFramework/Actor.h" -/// Interface for Carla actor spawners. -class IActorSpawner +#include "ActorSpawner.generated.h" + +/// Base class for Carla actor spawners. +UCLASS(Abstract) +class CARLA_API AActorSpawner : public AActor { + GENERATED_BODY() + public: - virtual ~IActorSpawner() {} + AActorSpawner(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) + { + PrimaryActorTick.bCanEverTick = false; + } /// Retrieve the list of actor definitions that this class is able to spawn. - virtual TArray MakeDefinitions() = 0; + virtual TArray MakeDefinitions() { + unimplemented(); + return {}; + } /// Spawn an actor based on @a ActorDescription and @a Transform. /// @@ -29,5 +41,8 @@ public: /// definitions retrieved with MakeDefinitions. virtual FActorSpawnResult SpawnActor( const FTransform &SpawnAtTransform, - const FActorDescription &ActorDescription) = 0; + const FActorDescription &ActorDescription) { + unimplemented(); + return {}; + } }; diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawnerBlueprintBase.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawnerBlueprint.h similarity index 75% rename from Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawnerBlueprintBase.h rename to Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawnerBlueprint.h index af7769aa3..a75db3eaa 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawnerBlueprintBase.h +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Actor/ActorSpawnerBlueprint.h @@ -9,27 +9,27 @@ #include "Carla/Actor/ActorSpawner.h" #include "Carla/Actor/ActorSpawnResult.h" -#include "ActorSpawnerBlueprintBase.generated.h" +#include "GameFramework/Actor.h" -/// Base class for Blueprints implementing IActorSpawner interface. +#include "ActorSpawnerBlueprint.generated.h" + +/// Base class for Blueprints implementing AActorSpawner interface. /// /// Blueprints deriving from this class are expected to override /// GenerateDefinitions and SpawnActor functions. -UCLASS(BlueprintType, Blueprintable) -class CARLA_API AActorSpawnerBlueprintBase - : public AActor, - public IActorSpawner +UCLASS(Abstract, BlueprintType, Blueprintable) +class CARLA_API AActorSpawnerBlueprint : public AActorSpawner { GENERATED_BODY() public: - virtual TArray MakeDefinitions() final + TArray MakeDefinitions() final { return GenerateDefinitions(); } - virtual FActorSpawnResult SpawnActor( + FActorSpawnResult SpawnActor( const FTransform &SpawnAtTransform, const FActorDescription &ActorDescription) final { diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEpisode.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEpisode.h index 7c55fea96..8a24220ef 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEpisode.h +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaEpisode.h @@ -31,7 +31,7 @@ public: return MapName; } - void RegisterActorSpawner(IActorSpawner &ActorSpawner) + void RegisterActorSpawner(AActorSpawner &ActorSpawner) { ActorDispatcher.Bind(ActorSpawner); } diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/TheNewCarlaGameModeBase.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/TheNewCarlaGameModeBase.cpp index cdbf2d8ac..b762837b8 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/TheNewCarlaGameModeBase.cpp +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/TheNewCarlaGameModeBase.cpp @@ -61,15 +61,15 @@ void ATheNewCarlaGameModeBase::SpawnActorSpawners() auto *World = GetWorld(); check(World != nullptr); - for (auto &SpawnerClass : BlueprintSpawners) + for (auto &SpawnerClass : ActorSpawners) { if (SpawnerClass != nullptr) { - auto *Spawner = World->SpawnActor(SpawnerClass); + auto *Spawner = World->SpawnActor(SpawnerClass); if (Spawner != nullptr) { Episode->RegisterActorSpawner(*Spawner); - BlueprintSpawnerInstances.Add(Spawner); + ActorSpawnerInstances.Add(Spawner); } else { diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/TheNewCarlaGameModeBase.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/TheNewCarlaGameModeBase.h index 23a5931d2..1412dcfa2 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/TheNewCarlaGameModeBase.h +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/TheNewCarlaGameModeBase.h @@ -6,7 +6,7 @@ #pragma once -#include "Carla/Actor/ActorSpawnerBlueprintBase.h" +#include "Carla/Actor/ActorSpawner.h" #include "Carla/Game/CarlaEpisode.h" #include "CoreMinimal.h" @@ -44,9 +44,11 @@ private: UPROPERTY() UCarlaEpisode *Episode = nullptr; + /// List of actor spawners that will be used to define and spawn the actors + /// available in game. UPROPERTY(EditAnywhere) - TArray> BlueprintSpawners; + TSet> ActorSpawners; UPROPERTY() - TArray BlueprintSpawnerInstances; + TArray ActorSpawnerInstances; }; diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Server/TheNewCarlaServer.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Server/TheNewCarlaServer.cpp index 55ec5ac24..eceb31ffd 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Server/TheNewCarlaServer.cpp +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Server/TheNewCarlaServer.cpp @@ -28,6 +28,12 @@ static std::vector MakeVectorFromTArray(const TArray &Array) return {Array.GetData(), Array.GetData() + Array.Num()}; } +static void AttachActors(AActor *Child, AActor *Parent) +{ + Child->AttachToActor(Parent, FAttachmentTransformRules::KeepRelativeTransform); + Child->SetOwner(Parent); +} + // ============================================================================= // -- FTheNewCarlaServer::FPimpl ----------------------------------------------- // ============================================================================= @@ -64,6 +70,30 @@ private: RespondErrorStr("episode not ready"); } } + + auto SpawnActor(const FTransform &Transform, FActorDescription Description) + { + auto Result = Episode->SpawnActorWithInfo(Transform, std::move(Description)); + if (Result.Key != EActorSpawnResultStatus::Success) + { + RespondError(FActorSpawnResult::StatusToString(Result.Key)); + } + check(Result.Value.IsValid()); + return Result.Value; + } + + void AttachActors(FActorView Child, FActorView Parent) + { + if (!Child.IsValid()) + { + RespondError("unable to attach actor: child actor not found"); + } + if (!Parent.IsValid()) + { + RespondError("unable to attach actor: parent actor not found"); + } + ::AttachActors(Child.GetActor(), Parent.GetActor()); + } }; // ============================================================================= @@ -87,12 +117,24 @@ void FTheNewCarlaServer::FPimpl::BindActions() const cr::Transform &Transform, cr::ActorDescription Description) -> cr::Actor { RequireEpisode(); - auto Result = Episode->SpawnActorWithInfo(Transform, std::move(Description)); - if (Result.Key != EActorSpawnResultStatus::Success) - { - RespondError(FActorSpawnResult::StatusToString(Result.Key)); - } - return Result.Value; + return SpawnActor(Transform, Description); + }); + + Server.BindSync("spawn_actor_with_parent", [this]( + const cr::Transform &Transform, + cr::ActorDescription Description, + cr::Actor Parent) -> cr::Actor { + RequireEpisode(); + auto ActorView = SpawnActor(Transform, Description); + auto ParentActorView = Episode->GetActorRegistry().Find(Parent.id); + AttachActors(ActorView, ParentActorView); + return ActorView; + }); + + Server.BindSync("attach_actors", [this](cr::Actor Child, cr::Actor Parent) { + RequireEpisode(); + auto &Registry = Episode->GetActorRegistry(); + AttachActors(Registry.Find(Child.id), Registry.Find(Parent.id)); }); }