Getting Skeletal Sockets from a TSoftClassPtr passed from a blueprint

Heya,

First of all, I’m new to Unreal, so still learning.

I have a procedural generation tech built in C++ that basically combines different actors at their skeletal mesh’s different socket locations.

A good analogy would be Rooms. I have a different mesh with sockets for each Room, and each Room has a Room_BP with the static mesh and some triggers etc.

I also have a House Generator in C++ that procedurally can create interesting houses out of a combination of rooms.

I have an Actor Blueprint (House_BP) that contains a C++ Component HouseGenerator.
The House_BP has an Array of Soft Class Pointers that I configure with the different available Room_BP’s for that House.

This array is then received in C++ like so:


void
UtestActorComponent::PassingActorsToCpp(TArray<TSoftClassPtr<AActor>> theActors)
{
	for (TSoftClassPtr<AActor> actorPtr : theActors)
	{
		FString name = actorPtr.ToString();
		auto actor = NewObject<AActor>(GetTransientPackage(), *name);
		// Get all components attached to the actor
		TArray<UStaticMeshComponent*> Components;
		actor->GetComponents(Components, true);

		// Print the class names of all components
		for (UActorComponent* Component : Components)
		{
			UE_LOG(LogTemp, Log, TEXT("Component class: %s"), *Component->GetClass()->GetName());
		}
		continue;

Now, I’d expect actor->GetComponents() to list the components that are defined in the Blueprint Actor. But it is empty.

The Room BP that was sent to C++ does in fact have components:

image

And eventually all I need to achieve is to extract the Sockets setup in the Static Mesh before passing along control to my own procedural generation code.

I’m obviously missing something obvious but I can’t figure out what…

My plan is for a designer to setup the available rooms from a blueprint, kick it off to my C++ generation code that uses the known sockets and available rooms to give back a list of rooms that will be instantiated from within the blueprint.

Perhaps this plan is just plain stupid, I don’t know :slight_smile: But seriously, why can’t I get the components from the blueprints so I can query it for Sockets?

Thanks in advance!

Your call to NewObject<AActor>(GetTransientPackage(), *name); is creating a literal AActor, not one of your subclasses. You are passing a string to the second parameter so it’s using the NewObject signature where second parameter is the name of the actor, and not its class. Name is just a label like you see in editor’s world outliner :
image

Normally this is something you would achieve by accessing the CDO, instead of allocating a new object just to read its components. However it turns out that accessing blueprint components from CDO is considerably more difficult than it should, so your approach doesn’t sound so bad.

Actors are supposed to be constructed via SpawnActor, but that means placing them in the world and then destroying them, so it adds overhead and possible side effects which you don’t want there. I’m curious if using NewObject would do the job. Here’s something you can try :

auto ActorClass = actorPtr.Get();
auto Actor = NewObject<AActor>(GetTransientPackage(), ActorClass, NAME_None, RF_NoFlags, ActorClass->GetDefaultObject());
Actor->PostSpawnInitialize(FTransform::Identity, nullptr, nullptr, false, true, false);
// get components

If that doesn’t work, try the following :

auto ActorClass = actorPtr.Get();
FActorSpawnParameters Parms;
Parms.bNoFail = true;
auto Actor = GetWorld()->SpawnActor<AActor>(ActorClass, Parms);
// get components
// destroy when done
Actor->Destroy();

Thank you for taking the time to answer, very kind of you!

I had to change the actorPtr.Get() into actorPtr.LoadSynchronous(), which I guess makes sense but it’s hard to tell from the documentation which is nonexisting :smiley:
And then your first example failed with a crash inside the engine from PostSpawnInitialize(), but your second example with SpawnActor works perfectly!

For posterity if someone else finds this thread; here’s both the working C++ code you helped with, and the Blueprint version of the same thing.

void
UtestActorComponent::PassingActorsToCpp(TArray<TSoftClassPtr<AActor>> theActors)
{
	for (TSoftClassPtr<AActor> actorPtr : theActors)
	{
		auto actorClass = actorPtr.LoadSynchronous();
		FActorSpawnParameters Parms;
		Parms.bNoFail = true;
		if (auto actor = GetWorld()->SpawnActor<AActor>(actorClass, Parms))
		{
			if (UStaticMeshComponent* StaticMeshComponent = actor->FindComponentByClass<UStaticMeshComponent>())
			{
				// Get all sockets in the static mesh
				TArray<FName> SocketNames = StaticMeshComponent->GetAllSocketNames();
				// Print the names of all sockets in the static mesh
				for (const FName& SocketName : SocketNames)
				{
					auto trs = StaticMeshComponent->GetSocketTransform(SocketName);
					UE_LOG(LogTemp, Log, TEXT("Socket name: %s (%s)"), *SocketName.ToString(), *trs.ToString());
				}
			}
			actor->Destroy();
		}
	}
}