Improvements to actor spawners

This commit is contained in:
nsubiron 2018-07-25 11:47:57 +02:00
parent 60d866d5b6
commit 8ae2770e1a
15 changed files with 139 additions and 38 deletions

View File

@ -23,7 +23,7 @@ namespace client {
SharedPtr<Actor> Client::SpawnActor(
const ActorBlueprint &blueprint,
const Transform &transform) {
auto actor = Call<carla::rpc::Actor>("spawn_actor", blueprint.MakeActorDescription(), transform);
auto actor = Call<carla::rpc::Actor>("spawn_actor", transform, blueprint.MakeActorDescription());
return SharedPtr<Actor>(new Actor{actor, GetWorld()});
}

View File

@ -10,11 +10,12 @@
#include <sstream>
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<std::string>();
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<std::string>(), but it gives the wrong
/// string.
PyErr_SetString(PyExc_RuntimeError, ss.str().c_str());
}

View File

@ -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<FString> 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;

View File

@ -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<AActor> Class;
/// A list of comma-separated tags.
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString Tags;

View File

@ -23,6 +23,10 @@ struct FActorDescription
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString Id;
/// Class of the actor to be spawned.
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AActor> Class;
/// User selected variations of the actor. Note that at this point are
/// represented by non-modifiable attributes.
UPROPERTY(EditAnywhere, BlueprintReadWrite)

View File

@ -17,6 +17,7 @@ void FActorDispatcher::Bind(FActorDefinition Definition, SpawnFunctionType Funct
Definition.UId = static_cast<uint32>(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<EActorSpawnResultStatus, FActorView> FActorDispatcher::SpawnActor(
{
if ((Description.UId == 0u) || (Description.UId > static_cast<uint32>(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);
}

View File

@ -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<SpawnFunctionType> SpawnFunctions;
TArray<TSubclassOf<AActor>> Classes;
FActorRegistry Registry;
};

View File

@ -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");

View File

@ -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

View File

@ -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<FActorDefinition> MakeDefinitions() = 0;
virtual TArray<FActorDefinition> 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 {};
}
};

View File

@ -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<FActorDefinition> MakeDefinitions() final
TArray<FActorDefinition> MakeDefinitions() final
{
return GenerateDefinitions();
}
virtual FActorSpawnResult SpawnActor(
FActorSpawnResult SpawnActor(
const FTransform &SpawnAtTransform,
const FActorDescription &ActorDescription) final
{

View File

@ -31,7 +31,7 @@ public:
return MapName;
}
void RegisterActorSpawner(IActorSpawner &ActorSpawner)
void RegisterActorSpawner(AActorSpawner &ActorSpawner)
{
ActorDispatcher.Bind(ActorSpawner);
}

View File

@ -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<AActorSpawnerBlueprintBase>(SpawnerClass);
auto *Spawner = World->SpawnActor<AActorSpawner>(SpawnerClass);
if (Spawner != nullptr)
{
Episode->RegisterActorSpawner(*Spawner);
BlueprintSpawnerInstances.Add(Spawner);
ActorSpawnerInstances.Add(Spawner);
}
else
{

View File

@ -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<TSubclassOf<AActorSpawnerBlueprintBase>> BlueprintSpawners;
TSet<TSubclassOf<AActorSpawner>> ActorSpawners;
UPROPERTY()
TArray<AActorSpawnerBlueprintBase *> BlueprintSpawnerInstances;
TArray<AActorSpawner *> ActorSpawnerInstances;
};

View File

@ -28,6 +28,12 @@ static std::vector<T> MakeVectorFromTArray(const TArray<Other> &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));
});
}