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);