Allow Blueprint Classes to derive from UPrimaryDataAsset

The Asset Management Framework / Asset Registry is almost fully usable solely in Blueprints, however, it’s missing the ability to make Primary Assets in shipping builds. The editor is able to ‘guess’ these tags using the ‘Should Guess Type And Name In Editor’ option which works great in non-shipping builds, but there is no way to correct for this in shipping builds using solely Blueprint.

I believe the correct approach would be to allow defining of UPrimaryDataAsset classes with Blueprint, but I don’t know if that is the best course of action. Alternatively, it would be nice if ‘Should Guess Type and Name In Editor’ would be able to work in shipping builds. I’m really hoping for just any way to use this system without C++ (so that it can be used in marketplace BP assets), so any change or info to make this possible would be greatly appreciated.

While the change looks good on te surface I don’t think it is viable in long term.
What makes PrimaryDataAsset Primary are these two overrides:




ENGINE_API virtual FPrimaryAssetId GetPrimaryAssetId() const override;
ENGINE_API virtual void PostLoad() override;



Especially the first one. By overriding these functions in C++ you turn any class into primary asset (which I have done for Abilities and Effects).
What whould be really nice change is to extract this functionality into seprate interaface and letit be implementable from C++ and Blueprints.

I just added support for this to UE 4.19 (it missed the 4.18 date and was too risky to shove in last minute). It’s not as simple as making PrimaryDataAsset blueprintable as the default behavior for GetPrimaryAssetId didn’t cover blueprint-only well.

To make this work in earlier versions you need to add Blueprintable to the UCLASS() for UPrimaryDataAsset, and change GetPrimaryAssetId to the following code:


FPrimaryAssetId UPrimaryDataAsset::GetPrimaryAssetId() const
{
    if (HasAnyFlags(RF_ClassDefaultObject))
    {
        UClass* BestPrimaryAssetTypeClass = nullptr;
        UClass* SearchPrimaryAssetTypeClass = GetClass()->GetSuperClass();

        // If this is a native class or immediate child of PrimaryDataAsset, return invalid as we are a type ourselves
        if (GetClass()->HasAnyClassFlags(CLASS_Native | CLASS_Intrinsic) || SearchPrimaryAssetTypeClass == UPrimaryDataAsset::StaticClass())
        {
            return FPrimaryAssetId();
        }

        // Starting with parent, look up the hierarchy for a class that is either native, or a blueprint class immediately below PrimaryDataAsset
        while (SearchPrimaryAssetTypeClass)
        {
            if (SearchPrimaryAssetTypeClass->GetSuperClass() == UPrimaryDataAsset::StaticClass())
            {
                // If our parent is this base class, return this as the best class
                BestPrimaryAssetTypeClass = SearchPrimaryAssetTypeClass;
                break;
            }
            else if (SearchPrimaryAssetTypeClass->HasAnyClassFlags(CLASS_Native | CLASS_Intrinsic))
            {
                // Our parent is the first native class found, use that
                BestPrimaryAssetTypeClass = SearchPrimaryAssetTypeClass;
                break;
            }
            else
            {
                SearchPrimaryAssetTypeClass = SearchPrimaryAssetTypeClass->GetSuperClass();
            }
        }

        if (BestPrimaryAssetTypeClass)
        {
            // If this is a native class use the raw name if it's a blueprint use the package name as it will be missing _C
            FName PrimaryAssetType = BestPrimaryAssetTypeClass->HasAnyClassFlags(CLASS_Native | CLASS_Intrinsic) ? BestPrimaryAssetTypeClass->GetFName() : FPackageName::GetShortFName(BestPrimaryAssetTypeClass->GetOutermost()->GetFName());

            return FPrimaryAssetId(PrimaryAssetType, FPackageName::GetShortFName(GetOutermost()->GetFName()));
        }

        // No valid parent class found, return invalid
        return FPrimaryAssetId();
    }


    // Data assets use Class and ShortName by default, there's no inheritance so class works fine
    return FPrimaryAssetId(GetClass()->GetFName(), GetFName());
}

Adding primary assets from marketplace content is still going to be difficult, because I can’t think of a clean way to avoid the ini-editing step, which has to be game specific. But after this change BP-only projects can support primary assets by creating a BP subclass of PrimaryDataAsset. The PrimaryAssetType will be the name of the blueprint that immediately subclasses PrimaryDataAsset, which you would then subclass again to create an instance.

Thanks Ben! Really appreciate it. For marketplace assets that are ‘complete projects’ (i.e. ship with configs) this will be fantastic. For those without, I’ve found that if instead you look up a primary asset list supplying an FName (by making a primary asset struct) instead of using the pin’s BP dropdown that lists available tags, the ‘integration’ step for end users is to simply edit their project settings which I believe is ‘good enough’ for now.

Edit: Quoting forum users with brackets in their name i.e. [EPIC] totally doesn’t work like it should.

I made a BP with PrimaryDataAsset as the parent called Leader_Flipbooks_PDA. Then I made some DataAssets with Leader_Flipbooks_PDA as the data asset type. In Project Settings>Asset Manager, what do I put as Primary Asset Type and Asset Base Class? And do I check Has Blueprint Classes?