How to create a clone of an actor from a CDO at runtime

Hello!

I need help with trying to create an actor at runtime who is a copy of a much more expensive actor, the idea is that this would represent a preview of something the player can choose to take control over later in the game, think of something like when setting up your character in Fortnite in the waiting lobby before deploying into the game, we can’t afford to have an instance of the entire character BP in this preview state, just a barebones version with only meshes (static, skeletal, etc…) and a few other components that allow for customisation.

We thought we could load the CDO by storing it in a TSubclassOf and then doing GetDefaultObject, looking up at its scene components and copying them across as templates, but we keep finding issues that prevent us from achieving this, such as a component from a CDO cannot seemingly be used as a template when creating new components, and the CDO does not seem to have any concept of attachment rules so we cannot recreate the hierarchy.

tl;dr what would be the best/recommended way to, at runtime, create an actor that is a copy of a CDO with just meshes/selective components whilst also recreating the same attachment hierarchy? Ideally without having to load/create an instance of the expensive object to begin with

Steps to Reproduce
N/A

Hello [mention removed]​,

Apologies for the delayed response. I’ve looked into your case and have a working solution for UE 5.5.

You can create a clone of an actor from its CDO by taking a source actor class and selectively copying only the components you want. This is done by defining a list of allowed component classes. You can then use FProperty::CopyCompleteValue_InContainer to properly duplicate the property values from the source component to the cloned component.

To preserve the parent-child hierarchy for scene components, you iterate over the cloned components and attach them to the corresponding cloned parent if it exists, or to a root component otherwise. The AttachParent/AttachChildren properties are not copied directly, since the hierarchy is restored manually using SetupAttachment.

Finally, the cloned components need to be registered to make them functional in the scene.

Here is a working example for a PreviewCloneActor that clones another existing actor at runtime:

APreviewCloneActor.h

UCLASS()
class CLONEACTORCASE_API APreviewCloneActor : public AActor
{
	GENERATED_BODY()
	
public:	
	APreviewCloneActor();
 
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Preview")
	TObjectPtr<class USceneComponent> PreviewRoot;
 
	/*Clone only the allowed component classes from SourceActor into *this* actor.
	 * - Defaults come from the source component's archetype (BP template) first.
	 * - (Optional) Apply instance overrides on top.
	 */
	UFUNCTION(BlueprintCallable, Category = "Preview Clone")
	bool CloneFromSource(
		AActor* SourceActor,
		const TArray<TSubclassOf<UActorComponent>>& AllowedComponentClasses,
		bool bIncludeInstanceOverrides = true);
};

APreviewCloneActor.cpp

// PreviewCloneActor.cpp
#include "PreviewCloneActor.h"
 
// Copy properties excluding attachment fields
static void CopyPropertiesSameClassFiltered(const UObject* Src, UObject* Dst)
{
	if (!Src || !Dst || Src->GetClass() != Dst->GetClass()) return;
 
	static const FName NAME_AttachParent(TEXT("AttachParent"));
	static const FName NAME_AttachChildren(TEXT("AttachChildren"));
	static const FName NAME_AttachSocketName(TEXT("AttachSocketName"));
 
	for (TFieldIterator<FProperty> It(Src->GetClass(), EFieldIterationFlags::IncludeSuper); It; ++It)
	{
		// Skip things the engine doesn't normally duplicate/serialize
		FProperty* Prop = *It;
		if (Prop->HasAnyPropertyFlags(CPF_Transient | CPF_DuplicateTransient | CPF_SkipSerialization))
			continue;
 
		// Skip attachment relationships for scene components (this is handled separately)
		if (Src->IsA<USceneComponent>() && (Prop->GetFName() == NAME_AttachParent || Prop->GetFName() == NAME_AttachChildren || Prop->GetFName() == NAME_AttachSocketName))
			continue;
 
		// Copy the property value
		Prop->CopyCompleteValue_InContainer(Dst, Src);
	}
}
 
APreviewCloneActor::APreviewCloneActor()
{
	PrimaryActorTick.bCanEverTick = false;
 
	PreviewRoot = CreateDefaultSubobject<USceneComponent>(TEXT("PreviewRoot"));
	SetRootComponent(PreviewRoot);
}
 
bool APreviewCloneActor::CloneFromSource(
    AActor* SourceActor,
    const TArray<TSubclassOf<UActorComponent>>& AllowedComponentClasses,
    bool bIncludeInstanceOverrides)
{
    if (!SourceActor || !PreviewRoot)
        return false;
 
	// Collect allowed components from source actor
    TArray<UActorComponent*> AllowedComps;
    for (UActorComponent* Comp : SourceActor->GetComponents())
    {
        if (!Comp)
            continue;
 
        for (const TSubclassOf<UActorComponent>& AllowedClass : AllowedComponentClasses)
        {
            if (Comp->IsA(*AllowedClass))
            {
                AllowedComps.Add(Comp);
                break;
            }
        }
    }
 
	// Clone each allowed component
    TMap<const UActorComponent*, UActorComponent*> SourceToClone;
    for (UActorComponent* SrcComp : AllowedComps)
    {
        UActorComponent* DstComp = NewObject<UActorComponent>(
            this,
            SrcComp->GetClass(),
            MakeUniqueObjectName(this, SrcComp->GetClass(), SrcComp->GetFName()),
            RF_Transient
        );
 
        if (!DstComp)
            continue;
 
        // Copy BP template properties
        if (const UObject* Archetype = SrcComp->GetArchetype())
            CopyPropertiesSameClassFiltered(Archetype, DstComp);
 
        // Optionally copy instance overrides
        if (bIncludeInstanceOverrides)
            CopyPropertiesSameClassFiltered(SrcComp, DstComp);
 
        // Configure primitive component properties
        if (UPrimitiveComponent* Prim = Cast<UPrimitiveComponent>(DstComp))
        {
            Prim->SetGenerateOverlapEvents(false);
            Prim->SetCollisionEnabled(ECollisionEnabled::NoCollision);
        }
 
        SourceToClone.Add(SrcComp, DstComp);
    }
 
    // Recreate hierarchy (for derived SceneComponents)
    for (UActorComponent* SrcComp : AllowedComps)
    {
        USceneComponent* SrcScene = Cast<USceneComponent>(SrcComp);
        USceneComponent* DstScene = Cast<USceneComponent>(SourceToClone[SrcComp]);
        if (!SrcScene || !DstScene)
            continue;
 
        // Attach to cloned parent if it exists, else attach to PreviewRoot
        USceneComponent* SrcParent = SrcScene->GetAttachParent();
        if (SrcParent && SourceToClone.Contains(SrcParent))
        {
            DstScene->SetupAttachment(Cast<USceneComponent>(SourceToClone[SrcParent]));
        }
        else
        {
            DstScene->SetupAttachment(PreviewRoot);
        }
    }
 
	// Register cloned components
    for (auto& Pair : SourceToClone)
    {
        if (UActorComponent* DstComp = Pair.Value)
        {
            if (!DstComp->IsRegistered())
                DstComp->RegisterComponent();
        }
    }
 
    return true;
}

With this approach you can control which components are cloned and the parent-child hierarchy is preserved for scene components.

The cloning method also includes an optional parameter to apply instance-specific overrides, allowing the clone to inherit runtime changes from the level instead of just the default template values.

This workflow has been tested on a simple Blueprint containing multiple components, with cloning limited to static meshes.

[Image Removed]

[Image Removed]

Please let me know if this information helps.

Best,

Francisco

Hi [mention removed]​ !

Thanks for the detailed response, this is an excellent code example and we’re actually taking this on board for another feature!

For this specific use case however, this is about 90% of the way there as we don’t have the source actor in the world, because these are actors the player is going to play the game with, we can’t afford to have them all loaded at the same time in this selection screen, so ideally we’d like to only load and clone from the its CDO, in other words `CloneFromSource` would look like this:

bool CloneFromSource( 
  TSubclassOf<AActor> SourceActorCDO, // This is the change.
  const TArray<TSubclassOf<UActorComponent>>& AllowedComponentClasses,
  bool bIncludeInstanceOverrides = true);

Currently we have a flow where the player is on a selection screen, cycles through various actors, and then chooses one to go into the game with. We don’t want to spawn an actor that is a full instance of the actors they are going into the game with all the gameplay elements as its too expensive, and those actors don’t exist in the world.

Here’s a rough code example of what we’re trying to achieve, the code isn’t perfect and I’m not doing any safety checks for simplicity:

UCLASS()
class CLONEACTORCASE_API AActorSelectionScreen : public AActor
{
 
public:
 
  // At some point the player chooses to preview an actor on the screen, which triggers a delegate that fires this method.
  void OnActorSelected(const TSubclassOf<AActor> SelectedActor);
 
private: 
 
  // We don't actually store this here, but to explain this example, imagine there is an array of all the possible actors to choose from somewhere in the game.
  TArray<TSubclassOf<AActor>> PossibleActors = {};
 
  // We also store the created preview to use later.
  TObjectPtr<APreviewCloneActor> PreviewActor;
 
}
#include "ActorSelectionScreen.h"
 
void AActorSelectionScreen::OnActorSelected(const TSubclassOf<AActor> SelectedActor) {
  PreviewActor = GetWorld()->SpawnActor(SelectedActor); 
 
  // We would pull the allowed components from somewhere here too and pass it in.
  const TArray<TSubclassOf<UActorComponent>> AllowedComponentClasses;
 
  PreviewActor->CloneFromSource(SelectedActor, AllowedComponentClasses);
}

Then later on, in the CloneFromSource method you provided, we would do ActorCDO->GetDefaultSubObject() to load the CDO and use that to read the actor data for the code you provided.

Is this possible, or because the default sub object does not have any attachments setup there is no way to copy components including the hierarchy?

Hello [mention removed]​,

Glad to hear the solution helped. You’re right, when using the DefaultSubObject, you wouldn’t get the same hierarchy data as you would if the selected actor was instanced in the level since AttachParent is not available.

However, you can still read the hierarchy defined in the BP (if the SelectedActor is a BP class) through the SimpleConstructionScript in the BlueprintGeneratedClass of your SelectedActor. The SCS Nodes contain the hierarchy information but you need to traverse the tree starting from the root node. Each node must be validated to see if it is a component template and in the list of allowed components. The children of each node should be read and this process should continue until the entire tree is processed.

For example, using the SelectedActor class you mentioned, here’s how you can navigate the hierarchy and keep track of both the allowed components and the parent node of each component for building the hierarchy:

    // Get the UBlueprintGeneratedClass (BGC) from the selected actor class
    UBlueprintGeneratedClass* BGC = Cast<UBlueprintGeneratedClass>(*SelectedActor);
    if (!BGC || !BGC->SimpleConstructionScript)
        return false;
 
    // Get the root node of the Simple Construction Script (SCS)
    USCS_Node* RootNode = BGC->SimpleConstructionScript->GetDefaultSceneRootNode();
    if (!RootNode)
        return false;
 
    // Stack to traverse all nodes in SCS
    TArray<USCS_Node*> NodeStack;
    NodeStack.Add(RootNode); 
 
    // Store parent-child relationships (key:child, value:parent)
    TMap<USCS_Node*, USCS_Node*> NodeParentMap;
 
    // Allowed component templates
    TArray<USCS_Node*> AllowedComps;
 
    while (NodeStack.Num() > 0)
    {
        USCS_Node* Node = NodeStack.Pop(false);
        if (!Node) continue;
 
        // If this node is a component and allowed, add it to AllowedComps
        if (UActorComponent* Template = Node->ComponentTemplate)
        {
            bool bAllowed = (AllowedComponentClasses.Num() == 0); // Allow all if no restrictions
            if (!bAllowed)
            {
                for (const TSubclassOf<UActorComponent>& AllowedClass : AllowedComponentClasses)
                {
                    if (Template->IsA(*AllowedClass))
                    {
                        bAllowed = true;
                        break;
                    }
                }
            }
            if (bAllowed)
            {
                AllowedComps.Add(Node);
            }
        }
 
        // Push children of this node onto the stack to process them
        const TArray<USCS_Node*>& NodeChildren = Node->GetChildNodes();
        for (USCS_Node* Child : NodeChildren)
        {
            if (Child)
            {
                // Store the current node as the parent for the child
                NodeParentMap.Add(Child, Node);
 
                // Add child to the stack to be processed
                NodeStack.Add(Child);
            }
        }
    }

After the traversal, the components you want to clone are ready in the AllowedComps array and each parent can be accessed viathe NodeParentMap.

With this data, we can start the cloning process very similar to the CloneFromSource method. I have built a CloneFromBPClass that takes SelectedActor and a list of AllowedComponentClasses. I’ve attached the method here in this reply.

Please let me know if this helps.

Best,

Francisco