1 minute read

Intro

By default, Class Default Objects only contain their C++ components, and will not return Blueprint spawned components when calling AActor::GetComponents().

However, there is another way to retrieve them.

Retrieving blueprint spawned actors

We can easily get a list of blueprint components by iterating through the construction script’s nodes generated nodes:

bool UMyBlueprintFunctionLibrary::GetClassBlueprintComponents(TSubclassOf<UObject> ObjectClass, TArray<UActorComponent*>& OutComponents)
{
    if (!ensureAlwaysMsgf(ObjectClass, TEXT("Cannot get blueprint components of a null class.")))
    {
        return false;
    }

    if (UBlueprintGeneratedClass* BlueprintClass = Cast<UBlueprintGeneratedClass>(ObjectClass))
    {
        // Retrieve the hierarchy of blueprint classes for this CDO.
        TArray<const UBlueprintGeneratedClass*> BlueprintClasses;
        UBlueprintGeneratedClass::GetGeneratedClassesHierarchy(BlueprintClass, BlueprintClasses);
        for (const UBlueprintGeneratedClass* Class : BlueprintClasses)
        {
            if (Class->SimpleConstructionScript)
            {
                // And now we get the component from the nodes.
                TArray<USCS_Node*> CDONodes = Class->SimpleConstructionScript->GetAllNodes();
                for (USCS_Node* Node : CDONodes)
                {
                    UActorComponent* CDOComponent = Node->GetActualComponentTemplate(BlueprintClass);
                    OutComponents.Add(CDOComponent);
                }
            }
        }

        return true;
    }

    return false;
}

Usage

With the above function, we can easily retrieve a CDO’s C++ and Blueprint components.

AActor* ClassDefault = MyBlueprintClass->GetDefaultObject<AActor>();

TArray<UActorComponent*> Components;
ClassDefault->GetComponents(Components);
UMyBlueprintFunctionLibrary::GetClassBlueprintComponents(MyBlueprintClass, Components);

// Components will now contain both C++ and Blueprint spawned actors.

I’ve learned this neat trick while working for a level editor, where the class default object held “emplacements”, which were scene components used as positions where rooms would connect to each other. These components were blueprint spawned, and as such I needed to be able to get them from a CDO.