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);
Author James
Published
Categories Snippet
Views 1953
2