Improvements to actor spawners
This commit is contained in:
parent
60d866d5b6
commit
8ae2770e1a
|
@ -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()});
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
{
|
|
@ -31,7 +31,7 @@ public:
|
|||
return MapName;
|
||||
}
|
||||
|
||||
void RegisterActorSpawner(IActorSpawner &ActorSpawner)
|
||||
void RegisterActorSpawner(AActorSpawner &ActorSpawner)
|
||||
{
|
||||
ActorDispatcher.Bind(ActorSpawner);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue