DataAsset blueprints have the wrong NativeClass

UDataAsset’s ctor assigns the result of GetClass() to its NativeClass property. But this causes blueprint classes derived from UDataAsset to show the blueprint class as its “Native Class”. Based on the name of the field (i.e. “Native Class”) and the fact that the blueprint class of which it is an instance is already shown as the “Parent Class” in the upper right (FSimpleAssetEditor should probably call this simply “Class”, not Parent Class btw), I would expect the most derived native class to be shown as the value for “Native Class” in the tooltip.

To assign NativeClass, it should probably iterate up the class hierarchy and stop when it reaches a class that has the CLASS_Native class flag.

edit: I also needed to add some code to PostLoad() to fix up existing cases since the value of NativeClass is serialized. That property should probably also be marked transient so that if you change the native parent class, it doesn’t keep showing the old value in the tooltip.

Steps to Reproduce

  1. Create a blueprint class using UDataAsset or UPrimaryDataAsset as the parent class. (e.g. MyDataAssetBP)
  2. Create a data asset instance using your new blueprint class (e.g. MyDataAssetInstance)
  3. Hover over the new data asset and note the value it shows for “Native Class:”
  4. Native class will be shown as “My Data Asset BP”

Hi,

You shouldn’t be able to subclass UDataAsset in a blueprint directly since it’s not flagged as Blueprintable, but I was able to repro this with UPrimaryDataAsset. That said, this workflow is a bit outside our intended use case for data assets, so I see why we assume there won’t be a blueprint class in the inheritance chain. From the comment on UDataAsset:

// If you want data inheritance or a complicated hierarchy, Data Only Blueprint Classes should be created instead.And in UPrimaryDataAsset:

// With blueprint subclasses, use Data Only Blueprints (and not Data Asset instances) to properly handle data inheritance and updating the parent class.It wouldn’t be too difficult to update this to grab the most derived native class as you suggest, but I worry there may be other issues with this workflow since it strays from the intended use of data assets. Are you able to share some details on how you’re using data assets here? I can push a code review to the team for consideration, but it could be helpful to have some justification for why it’s necessary.

Best,

Cody

Hi,

That seems reasonable, we could update the UDataAsset constructor to something like this:

UClass* MostDerivedNativeClass = GetClass();
while (MostDerivedNativeClass)
{
	if (MostDerivedNativeClass->HasAnyClassFlags(CLASS_Native | CLASS_Intrinsic))
	{
		// Found native class
		break;
	}
	else
	{
		MostDerivedNativeClass = MostDerivedNativeClass->GetSuperClass();
	}
}
NativeClass = MostDerivedNativeClass;

I’ll run that by the team to see if there are any concerns.

Best,

Cody

Hi,

This approach works fine but could be further improved by storing this metadata at save time instead of adding it directly to the asset. We’ll likely remove the NativeClass property altogether in 5.8, if you were interested in that change then you can grab it once it’s available (UE-354392). For now, the solution here should be enough to ensure the proper native class shows up on your data assets.

Yeah, the case that I was looking at was using PrimaryDataAsset as the base class. Apologies for any confusion.

Where I see it most often used is in cases where we’d normally derive from UObject for some kind of metagame or framework class (like store content, or maybe dynamic configuration for pawns), but also want to leverage asset bundles for referencing content. We could create two separate classes, a data asset class and a logic class that uses the data asset, but it’s often more convenient to just combine those into a single type. So we end up with some blueprint base classes that derive from UPrimaryDataAsset (or a native class derived from UPrimaryDataAsset), which themselves have one or more child classes. Those blueprint child classes are what we use to create the data asset instances, and that’s where the problem exists.

In any case, showing a blueprint class as the value for “Native Class” when hovering those data asset instances seems like a bug. Especially so since UPrimaryDataAsset *is* intended to be subclassed by data-only blueprint classes.

Yeah, pretty much what I ended up doing here. Cheers