How do you get a default property of a blueprint generated class?

So I have a c++ defined class: AThingActor which is a child of AActor.
This AThingActor has the custom FString property foo.
I have created a blueprint child of AThingActor called ABlueprintThing
I want to tell the user all of the blueprint assets I have in my project that inherit from AThing, and to do so I want to tell them the property foo of each asset.

UCLASS()
class API AThingActor : public AActor
{
	GENERATED_BODY()

	public:

	UPROPERTY(EditDefaultsOnly)
	FString foo;
};

Here in this section I’m getting the derived classes of AThingActor, which will give me the names of all blueprints that inherit from it, so it should give me the name of the class of ABlueprintThing. (I’m not really sure what derived classes are or how they’re found as they seem significantly different to subclasses.)
After getting those I get all the UBlueprint assets in my project as ABlueprintThing is a blueprint.
Then I compare the generated class of the blueprint assets to the derived classes to determine which asset is ABlueprintThing.
Once I’ve found the asset that is ABlueprintThing, I want to find out the value of foo of that asset.

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

        // Get all of the subclasses (including blueprint generated) of AThingActor.
        TSet<FName> subclass_names;
        {
            FName thing_class_name = AThingActor::StaticClass()->GetFName();
            TArray<FName> base_class_names{thing_class_name};
            TSet<FName> excluded_class_names;
            asset_registry_module.Get().GetDerivedClassNames(base_class_names, excluded_class_names, subclass_names);
            subclass_names.Remove(thing_class_name);
        }

        // Get all blueprints assets .
        FARFilter filter;
        filter.ClassPaths.Add(FTopLevelAssetPath(UBlueprint::StaticClass()->GetClassPathName()));
        filter.bRecursivePaths = true;
        TArray<FAssetData> thing_data;
        asset_registry_module.Get().GetAssets(filter, thing_data);

        for (const auto& asset : thing_data) {
            // Compare generated assets classes with the found derived classes.
            FString generated_class_path_ptr = asset.TagsAndValues.FindRef(TEXT("GeneratedClass"));
            if (!generated_class_path_ptr.IsEmpty()) {
                const FString class_object_path= FPackageName::ExportTextPathToObjectPath(*generated_class_path_ptr);
                const FString class_name= FPackageName::ObjectPathToObjectName(class_object_path);

                if (subclass_names.Contains(*class_name)) {
                    UBlueprint* thing_blueprint = Cast<UBlueprint>(asset.GetAsset());
                    if (thing_blueprint) {                        
                        // Get the foo property.
                        }
                    }
                }
            }
        }

Finding out foo is where I get stuck. I can get the default object, by getting the static class of the generated class of the asset, but that has no properties associated. I can’t cast thing_blueprint to AThingActor or a subclass of it, as it’s a UBlueprint, and is unrelated to AThingActor.
Is there any way to find the value of foo?

If you are on UE5, you can use UAssetRegistryHelpers::GetBlueprintAssets to find blueprint subclasses, rather than walking the AR manually.

Other than that, I’m just gonna assume you are retrieving the correct assets after this

if (subclass_names.Contains(*class_name)) {

First off, I don’t recommend reaching the UBlueprint directly, because normally those are stripped off shipping builds during cook, so your code won’t work anymore in shipping.
But if this is an editor-only thing, then it’s fine and you can do the following :

UBlueprint* BP = Cast<UBlueprint>(asset.GetAsset());
UClass* Class = BP->GeneratedClass;
AThingActor* CDO = Class->GetDefaultObject<AThingActor>();
return CDO->foo;

If you need it to work in shipping, you need to reach the generated class directly without going through the UBlueprint. I can’t believe in 5.3 the engine still doesn’t provide a standardized function to do this, so you have to build its path manually :

auto ClassPath = FSoftClassPath(asset.GetObjectPathString() + "_C");
if (UClass* Class = ClassPath.TryLoadClass<AThingActor>())
{
    AThingActor* CDO = Class->GetDefaultObject<AThingActor>();
    return CDO->foo;
}
1 Like

This is very helpful thank you, it worked perfectly!
Thank you also for the information on what won’t work in a shipping version.

My only comment would be that I didn’t have to use the + “_c” as I’d already got the generated path by getting the generated path from the asset
FString generated_class_path_ptr = thing_asset.TagsAndValues.FindRef(TEXT("GeneratedClass"));
Then getting the object path of that:
FPackageName::ObjectPathToObjectName(generated_class_path_ptr);

Again I don’t know if this is the most sensible path, as it’s a lot of process compared to a string manipulation, but at least I didn’t have to manually twist something.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.