Get Assets by Class - how to find all BP assets of given class?

I’m working in blueprints with editor utilities and need names of all BPs of specific class. E.g. if I have a BP_ArrowBase and inheriting from it: BP_ArrowFIre, BP_ArrowIce. I’d like to get the names of the latter two.

I tried using ‘Get Assets by Class’ with BP_ArrowBase class but it returns nothing. Eventually I found that all blueprints report as being of Blueprint class. So I can get a list of all blueprints but then their class checking doesn’t work either:

In this example, among others, it prints names of BP_ArrowFire, BP_ArrowIce but the check BP_ArrowFire is Child of BP_ArrowBase returns false.

How else can I get a list of BP names or classes that inherit from BP_ArrowBase?

1 Like

Afaik this is not simple because assets/packages are not always loaded at all times, and asset registry does not know about blueprints hierarchical inheritance.

However, asset registry has ability to search by tags, and blueprints do store their parent class path and their closest native parent class path into tags.

Unfortunately searching by tags is not exposed to blueprints so you’ll need C++ utility to get anything done on that front.

Here is an example utility that searches blueprints children of a given native parent class :

#include "AssetRegistry/AssetRegistryModule.h"

bool USomeUtility::FindBlueprintsChildOf(TSubclassOf<UObject> InNativeClass, TArray<FAssetData>& FoundAssets)
{
	if (!InNativeClass || !InNativeClass->IsNative())
		return false;

	FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");

	TMultiMap<FName, FString> TagsValues;

	// All native CDOs should be in memory - we can look up native subclasses of InNativeClass
	for (FThreadSafeObjectIterator It(InNativeClass); It; ++It)	
	{
		UClass* Class = It->GetClass();
		if (Class->IsNative() && Class->ClassDefaultObject == *It)
		{
			TagsValues.AddUnique(FBlueprintTags::NativeParentClassPath, FString::Printf(TEXT("%s'%s'"), *Class->GetClass()->GetName(), *Class->GetPathName()));
		}
	}

	return AssetRegistryModule.Get().GetAssetsByTagValues(TagsValues, FoundAssets);
}

I am adding all native subclasses of the provided class, so if you supply “Pawn” it will effectively also look for “Character” subclasses and “SpectatorPawn” subclasses etc.

You can theoretically achieve something similar with a blueprint base class, using the FBlueprintTags::ParentClassPath tag instead, however if you also need to fetch grandchildren you’ll probably have to implement some additional recursion.

3 Likes

This option worked for me only after the replacement FString::Printf(TEXT("%s'%s'"), *Class->GetClass()->GetName(), *Class->GetPathName()) on FObjectPropertyBase::GetExportPath(Class)

My friend found a solution:

  • use node GetDerivedClassNames
  • for Name param connect Make TopLevelAssetPath node:
    • for Package Name param use folder path
    • for Asset Name param use BP name + “_C” suffix, e.g. MyBlueprint_C
2 Likes

Well, this is how I manage to get around the issue in Blueprints, not sure if there is a better way, but it worked for me.

2 Likes

Since UE5 there is a new node GetBlueprintAssets designed to do exactly that - find blueprint child classes of specified parent class (native or BP). It is also based on GetDerivedClassNames and does a recursive search using assets’ ParentClassPath tag.

2 Likes

Based on @Chatouille and @VernamRD answer:

Here the version which only requires a UClass* Base Class

	static FORCEINLINE_DEBUGGABLE bool GetBlueprintsFromBaseClass(UClass* InBaseClass,
	                                                         TArray<FAssetData>& FoundAssets)
	{
		if (!IsValid(InBaseClass))
		{
			return false;
		}
		
		FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(
			"AssetRegistry");

		TMultiMap<FName, FString> TagsValues;

		// All native CDOs should be in memory - we can look up native subclasses of InNativeClass
		for (FThreadSafeObjectIterator It(InBaseClass); It; ++It)
		{
			const UClass* Class = It->GetClass();
			if (Class->IsNative() && Class->ClassDefaultObject == *It)
			{
				TagsValues.AddUnique(FBlueprintTags::NativeParentClassPath,
				                     FObjectPropertyBase::GetExportPath(Class));
			}
		}

		return AssetRegistryModule.Get().GetAssetsByTagValues(TagsValues, FoundAssets);
	}

Usage:

TArray<FAssetData> AssetData;
GetBlueprintsFromBaseClass(MyUInheritUObjectClass::StaticClass(), AssetData);
2 Likes

This helped me sorting this logic out, which is exactly what I needed