Can I make instanced data assets based of another data asset?

Hi,

I have a data asset called UMyDataAsset that derives from UDataAsset. UMyDataAsset is defined in code and exposes a bunch of properties. Designers can make new instances of it in the editor and then these instances can be referenced as a property using TObjectPtr<UMyDataAsset> in various systems that need to read it.

There are times where design would like to be able to make minor modifications to this data asset for convenience. This means that the base becomes a sort of template, and then changes can be made to it. I imagine that if there are any changes applied we would construct a new inline instance (similar to the way Instanced uobjects work), which would have its data based off of the source instance of UMyDataAsset but with these additional overrides applied as necessary.

Is this possible to do in unreal? If not could you give me pointers that would allow me to add support for this,

Many thanks,

James

Steps to Reproduce
N/A

Hi there,

I’m not 100% I understand what you’re aiming for, so correct me if I’m wrong. It looks like you’re trying to have a UDataAsset be optionally inline editable (that is, you can specify a template data asset to use in a blueprint, or you can decide to override the template values directly in the editor panel of the blueprint?).

If this is what you want, then something like the following setup might work for you. This will let you specify a standard data asset, but optionally override it with an inline version that you can change in the blueprint details panel. You could also add a copy constructor to the data asset if you wanted to copy over the template values on checking the override boolean flag in editor:

Edit: Sorry copy constructors can’t be made for UObjects. To create a new inline editable instance when the override Boolean is checked, that uses your specified data asset as a template, call the NewObject function with the template argument set to your specific data asset. Do this in PostEditChangeProperty when the override Boolean gets set to true.

An example data asset class:

UCLASS(BlueprintType, EditInlineNew)

class LIGHTINJECTCRASH_API USomeDataAssetBase : public UDataAsset

{

GENERATED_BODY()

public:

UPROPERTY(EditAnywhere, BlueprintReadWrite);

FVector SomeVec1;

UPROPERTY(EditAnywhere, BlueprintReadWrite);

FVector SomeVec2;

UPROPERTY(EditAnywhere, BlueprintReadWrite);

bool SomeBool;

};

Example Actor .h:

UCLASS(Blueprintable)

class GAME_API ASomeActor : public AActor

{

GENERATED_BODY()

public:

// Sets default values for this actor’s properties

ASomeActor(const FObjectInitializer& FObjectInitializer);

protected:

// Called when the game starts or when spawned

virtual void BeginPlay() override;

public:

// Called every frame

virtual void Tick(float DeltaTime) override;

UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)

bool OverrideDataAsset = false;

UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta=(EditCondition = “!OverrideDataAsset”))

TObjectPtr<USomeDataAssetBase> SomeDataAsset;

UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Instanced, meta = (EditCondition=“OverrideDataAsset”))

TObjectPtr<USomeDataAssetBase> SomeDataAssetOverride;

#if WITH_EDITOR

virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;

#endif

private:

UPROPERTY()

TObjectPtr<USomeDataAssetBase> OriginalDataAsset;

};

in Actor .cpp:

#if WITH_EDITOR

void ASomeActor::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)

{

Super::PostEditChangeProperty(PropertyChangedEvent);

if (OverrideDataAsset)

{

if (!OriginalDataAsset)

{

OriginalDataAsset = SomeDataAsset;

}

SomeDataAsset = SomeDataAssetOverride;

}

if (!OverrideDataAsset && SomeDataAsset == SomeDataAssetOverride)

{

SomeDataAsset = OriginalDataAsset;

OriginalDataAsset = nullptr;

}

}

#endif

Let me know if you had something else in mind though.

Regards,

Lance Chaney.

Hey thanks for the info!

I did something similar in the end, but I made an instanced struct that wraps this (one for setting instanced data, and one for referencing the data asset directly). This allows design to choose what they want and we don’t serialize additional data unnecessarily. Additionally I have updated it so that my ‘data assets’ are actually constructed as blueprints instead (added editor tooling to make this hidden to design) so that when they make a new data asset, it actually makes a new blueprint where they edit the CDO when modifying it. This means that they can now create a new data asset, and then this can be used as the basis for any ‘instanced’ data.

This has identified another problem though. The data asset contains arrays. If any property is modified on this array in one of the instanced versions, it means the whole array is marked as modified rather than just the specific property for that entry in the array. If we now make any changes to the base types array data, none of those changes are propagated to the instance. For example if I have an array with two entries, then set any property in entry 1 in the instance, if I then make changes to entry 2 in the blueprint, those properties don’t propagate to the instance.

Is there any way around this?

That’s an interesting approach. I tried out something like I think you are doing, but couldn’t replicate your issue exactly with modifying array element values causing the whole array to be marked as different from the archetype. However, I did encounter an issue while setting this up that might be related, or causing your issue in a more indirect way. Here is my setup for discussion:

My actor contains a template class object and an instanced data asset as follows:

UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<UArrayInheritanceDataAsset> TemplateData = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced)
TObjectPtr<UArrayInheritanceDataAsset> InstancedData = nullptr;

In post edit change I do the following:

if (PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED(AArrayInheritanceTest, TemplateData))
{
    InstancedData = NewObject<UArrayInheritanceDataAsset>(this, TemplateData);
}

This simply creates a new instance data, from the template class object, whenever the template class property is changed in editor

The actual data asset simply contains an array of structs and, importantly, is marked with EditInlineNew:

UCLASS(EditInlineNew)
class PROJECT_API USomeDataAsset : public UPrimaryDataAsset
{
    GENERATED_BODY()
 
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    TArray<FSomeData> TestArray;
};

The struct contains the following properties:

UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bTestBool = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float TestFloat = 0.0f;

In editor I can now create a blueprint derived from the data asset, and / or create children of this data asset blueprint.

In the blueprint version of my actor I can then assign the TemplateData class as one of these derived data asset blueprints.

This then creates a new instance using the TemplateData class as the base class (this automatically sets the TemplateData CDO as the template as well).

IMPORTANT: EditInlineNew - If you don’t specify this property for the data asset class, you can get a nasty bug, where initially everything appears to be working. However, when you click the compile button on one of the derived data asset blueprints, this will replace the actual InstancedData pointer on your actor blueprint with a REINST.TRANSIENT version that seems to have all linkage to the CDO broken. So if you’re somehow triggering a recompile of you data asset blueprint (or just clicking this manually) after modifying an array element, then this could cause the issue you are seeing. Otherwise, there shouldn’t be any reason why modifying a TArray element property would cause the whole array to be marked as different from the archetype.

Hi Lance,

A few questions:

  • In your initial response you create a template data asset that you can edit in line (exactly what I’m looking for). I copied the code you had in the comment to test how this would work, however, I am not understanding how to add that data asset to the class I need to (in my case, an AI controller class) to edit it in-line of the controller that I want to. Are there any steps that you can line out to help with this?

    • This variable I created, I did so in BP. Do I need to make a C++ variable and specify specific UProperty specifiers?

    • EDIT: I combed through the rest of the messages and saw how you specified the properties as “EditAnywhere, BlueprintReadWrite, Instanced” which now correctly allows me to view a fresh data instance. For those that are curious, the UPROPERTY definition looks like:

      image
      resulting in:

  • Is there a way to map these data assets based on an enum?

    • I’m trying to create groups of the same variables that change depending on the aggression level of an AI agent. The agents will have 3 states and I don’t want to 3x all of the behavioral variables that I will be defining. I’m trying to create some sort of array that maps a certain aggression level with a certain instanced data asset.
      • i.e. The range at which my AI agents will attempt to rush another pawn will change based on a given aggression level.
    • Is this something that is possible?