Allows you to retrieve actor component configuration and data from a Blueprint Generated Class, similar to the Class Default Object for C++ Classes. There are optional optimizations for filtering by name, and for returning multiple components.
Single Component Implementation
const UActorComponent* UMyFunctionLibrary::GetClassComponentTemplate(FName& FoundComponentName, const TSubclassOf<AActor> ActorClass, const TSubclassOf<UActorComponent> ComponentClass, const FName ComponentName /*= NAME_None*/)
{
if (IsValid(ActorClass) && IsValid(ComponentClass))
{
UBlueprintGeneratedClass* CurrentBPClass = Cast<UBlueprintGeneratedClass>(ActorClass);
while (CurrentBPClass)
{
// Not all BP's have an SCS (apparently)
if (CurrentBPClass->SimpleConstructionScript)
{
if (ComponentName != NAME_None)
{
// Find Matching Node
const USCS_Node* FoundNode = CurrentBPClass->SimpleConstructionScript->FindSCSNode(ComponentName);
if (FoundNode && FoundNode->ComponentClass->IsChildOf(ComponentClass))
{
if (const UActorComponent* FoundComponent = FoundNode->GetActualComponentTemplate(CurrentBPClass))
{
FoundComponentName = ComponentName;
return FoundComponent;
}
}
}
else
{
// No specified name, just return first component class that matches.
const TArray<USCS_Node*>& AllSCSNodes = CurrentBPClass->SimpleConstructionScript->GetAllNodes();
for (const USCS_Node* NodeItr : AllSCSNodes)
{
if (NodeItr->ComponentClass->IsChildOf(ComponentClass))
{
FoundComponentName = NodeItr->GetVariableName();
return NodeItr->GetActualComponentTemplate(CurrentBPClass);
}
}
}
}
// Didn't find an explicit entry in this class, check inherited component overrides.
if (UInheritableComponentHandler* Handler = CurrentBPClass->GetInheritableComponentHandler(false))
{
if (ComponentName != NAME_None)
{
const FComponentKey FoundKey = Handler->FindKey(ComponentName);
if (FoundKey.IsValid())
{
if (const UActorComponent* FoundComponent = Handler->GetOverridenComponentTemplate(FoundKey))
{
FoundComponentName = ComponentName;
return FoundComponent;
}
}
}
else
{
TArray<FComponentOverrideRecord>::TIterator InheritedComponentItr = Handler->CreateRecordIterator();
for (InheritedComponentItr; InheritedComponentItr; ++InheritedComponentItr)
{
const FComponentOverrideRecord& Record = *InheritedComponentItr;
if (Record.ComponentClass->IsChildOf(ComponentClass) && Record.ComponentTemplate)
{
FoundComponentName = Record.ComponentKey.GetSCSVariableName();
return Record.ComponentTemplate;
}
}
}
}
// Wasn't found in this class or it's inherited vars. Check the Super BP Class
CurrentBPClass = Cast<UBlueprintGeneratedClass>(CurrentBPClass->GetSuperClass());
}
// If we get this far, didn't find a Blueprint template. Get the component properties from the native CDO if possible.
if (const AActor* CDO = ActorClass.GetDefaultObject())
{
if (ComponentName == NAME_None)
{
if (const UActorComponent* FoundComponent = CDO->FindComponentByClass(ComponentClass))
{
FoundComponentName = ComponentName;
return FoundComponent;
}
}
else
{
for (const UActorComponent* ComponentItr : CDO->GetComponents())
{
if (ComponentItr->GetFName() == ComponentName && ComponentItr->GetClass()->IsChildOf(ComponentClass))
{
FoundComponentName = ComponentItr->GetFName();
return ComponentItr;
}
return nullptr;
}
}
}
}
FoundComponentName = NAME_None;
return nullptr;
}
Multiple Component Implementation
TArray<UActorComponent*> UST_CoreStatics::GetClassComponentTemplates(TArray<FName>& FoundComponentNames, const TSubclassOf<AActor> ActorClass, const TSubclassOf<UActorComponent> ComponentClass)
{
/* Use a Map to avoid reinsertion of the same overridden template in a parent */
TMap<FName, UActorComponent*> ComponentMap = {};
// Check Classes
if (IsValid(ActorClass) && IsValid(ComponentClass))
{
// Insert Native Components First
if (const AActor* CDO = ActorClass.GetDefaultObject())
{
for (UActorComponent* ComponentItr : CDO->GetComponents())
{
if (ComponentItr->GetClass()->IsChildOf(ComponentClass))
{
ComponentMap.Emplace(ComponentItr->GetFName(), ComponentItr);
}
}
}
// Now find Blueprint Components and/or overrides
UBlueprintGeneratedClass* BPClass = Cast<UBlueprintGeneratedClass>(ActorClass);
if (BPClass)
{
// Move to the Top-Most BP Class, then move downards
TArray<UBlueprintGeneratedClass*, TInlineAllocator<4>> GeneratedClasses;
while (BPClass)
{
GeneratedClasses.Emplace(BPClass);
BPClass = Cast<UBlueprintGeneratedClass>(BPClass->GetSuperClass());
}
// Now go from top-most parent to lowest descendant, checking for overrides on the way
for (int32 Idx = GeneratedClasses.Num() - 1; Idx >= 0; Idx--)
{
// Not all BP's have an SCS (apparently)
UBlueprintGeneratedClass* CurrentBPClass = Cast<UBlueprintGeneratedClass>(ActorClass);
if (CurrentBPClass->SimpleConstructionScript)
{
// No specified name, just return first component class that matches.
const TArray<USCS_Node*>& AllSCSNodes = CurrentBPClass->SimpleConstructionScript->GetAllNodes();
for (const USCS_Node* NodeItr : AllSCSNodes)
{
if (NodeItr->ComponentClass->IsChildOf(ComponentClass))
{
ComponentMap.FindOrAdd(NodeItr->GetVariableName(), NodeItr->GetActualComponentTemplate(CurrentBPClass));
}
}
}
// Search Inherited Components
if (UInheritableComponentHandler* Handler = CurrentBPClass->GetInheritableComponentHandler())
{
TArray<FComponentOverrideRecord>::TIterator InheritedComponentItr = Handler->CreateRecordIterator();
for (InheritedComponentItr; InheritedComponentItr; ++InheritedComponentItr)
{
const FComponentOverrideRecord& Record = *InheritedComponentItr;
if (Record.ComponentClass->IsChildOf(ComponentClass) && Record.ComponentTemplate)
{
ComponentMap.FindOrAdd(Record.ComponentKey.GetSCSVariableName(), Record.ComponentTemplate);
}
}
}
}
}
}
ComponentMap.GenerateKeyArray(FoundComponentNames);
TArray<UActorComponent*> ReturnVal = {};
ComponentMap.GenerateValueArray(ReturnVal);
return ReturnVal;
}
Header Declarations
/* * Gets the template component on the actor class with the given component class. * Supports Blueprint Classes * * @param ActorClass - The Actor Blueprint/Native Class * @param ComponentClass - The expected type of actor component. * @param ComponentName - If specified, makes the search faster and only includes components with the given name. */ UFUNCTION(BlueprintCallable, Category = "Helpers", meta = (DeterminesOutputType = "ComponentClass")) static const UActorComponent* GetClassComponentTemplate(FName& FoundComponentName, const TSubclassOf<AActor> ActorClass, const TSubclassOf<UActorComponent> ComponentClass, const FName ComponentName = NAME_None); /* * Gets all template components on the actor class with the given component class. * Supports Blueprint Classes * * @param ActorClass - The Actor Blueprint/Native Class * @param ComponentClass - The expected type of actor component. */ UFUNCTION(BlueprintCallable, Category = "Helpers", meta = (DeterminesOutputType = "ComponentClass")) static TArray<UActorComponent*> GetClassComponentTemplates(TArray<FName>& FoundComponentNames, const TSubclassOf<AActor> ActorClass, const TSubclassOf<UActorComponent> ComponentClass);