Is it possible to edit a nested instanced object in editor?

Hi!

I’m trying to a data asset with nested instanced properties. Using a single instanced property is fine, it works great for all of my needs. If the first instanced object has its own instanced property, I do not see the updates in the data asset any more. This is causing users to have issues where they change the data in the instanced property but the data asset does not match.

This exists for both Object pointers with the Instanced property or FInstancedStruct.

First of all, is this behavior possible? I assume the property panel isn’t listening to nested instanced property updates or maybe it’s an issue with serialization?

If this is not possible, I’d like to be able to still pick an object type and then edit it inline. This could point to a single non-instanced struct or an object that is spawned manually and then serialized so that value can be changed inline. Is that possible? I see that the ChildActorComponent has somewhat similar functionality to this but it also has it’s own property drawer to accomodate that and it may be outdated now that we have tools like FInstancedStruct.

Steps to Reproduce

  • Create a data asset with an instanced property
  • Within that instanced property, create another instanced property
  • See that when you change the second nested property, the property is not updated in the data asset view

Hi [mention removed]​,

Digging a bit into this to find the cause. For now I have not been able to trigger your nested instanced issues. I’ve tested these different scenarios:

With FInstancedStruct:

USTRUCT(BlueprintType)
struct TPP5_6_API FMyStrategyA
{
	GENERATED_BODY()
 
	UPROPERTY(EditAnywhere, Category="Strategy")
	int32 Number = 7;
 
	UPROPERTY(EditAnywhere, Category="Strategy")
	FString Label = TEXT("A");
 
	UPROPERTY(EditAnywhere, Category="Strategy")
	FInstancedStruct InstancedStruct; 
};
 
USTRUCT(BlueprintType)
struct TPP5_6_API FMyStrategyB
{
	GENERATED_BODY()
 
	UPROPERTY(EditAnywhere, Category="Strategy")
	float Multiplier = 2.0f;
 
	UPROPERTY(EditAnywhere, Category="Strategy")
	bool bEnabled = true;
};
 
UCLASS(BlueprintType)
class TPP5_6_API UMyStructConfigAsset : public UDataAsset
{
	GENERATED_BODY()
public:
 
	UPROPERTY(EditAnywhere, Category="Config")
	FInstancedStruct Strategy;
}

[Image Removed]

And the same for UObjects:

UCLASS(EditInlineNew, DefaultToInstanced, BlueprintType)
class TPP5_6_API UMyInnerSettings : public UObject
{
	GENERATED_BODY()
 
public:
	UPROPERTY(EditAnywhere, Category="Inner")
	int32 Number = 0;
 
	UPROPERTY(EditAnywhere, Category="Inner")
	FString Label = TEXT("Default");
};
 
UCLASS(EditInlineNew, DefaultToInstanced, BlueprintType)
class TPP5_6_API UMyOuterThing : public UObject
{
	GENERATED_BODY()
 
public:
	// Nested instanced object (the troublemaker)
	UPROPERTY(EditAnywhere, Instanced, Category="Outer")
	UMyInnerSettings* Settings = nullptr;
 
	// A plain field so you can see "outer-level" edits do save fine
	UPROPERTY(EditAnywhere, Category="Outer")
	float Multiplier = 1.0f;
};
 
UCLASS(BlueprintType, Blueprintable)
class TPP5_6_API UMyConfigAsset : public UDataAsset
{
	GENERATED_BODY()
 
public:
	// Top-level instanced object (generally reliable)
	UPROPERTY(EditAnywhere, Instanced, Category="Config", meta=(DisplayName="Outer Thing"))
	UMyOuterThing* Outer = nullptr;
 
	// Mirrors a value so you can see whether the asset serialized
	UPROPERTY(EditAnywhere, Category="Config")
	int32 MirrorOfInnerNumber = -1;
};

[Image Removed]

Would be great if you could provide or explain a bit more even with a code sample about your issue so I can reproduce it directly and look a work around for it.

Just as an plus, when devs want to expand on how the panel is drawn, or want to add extra information, unreal gives a lot of tools on how to expand it so that it fits your needs.

Oficial docuemntation about Details Panel Customization: https://dev.epicgames.com/documentation/en-us/unreal-engine/details-panel-customizations-in-unreal-engine

Lastly, Unreal Engine has also a function to detect when a property in a uasset changes and react to it. As the issue you have it looks like it is happening in all UDataAssets, I don’t think it will make you a difference, but it might help for other cases:

UObject::PostEditChangeProperty( struct FPropertyChangedEvent& PropertyChangedEvent);
 
UObject::PostEditChangeChainProperty( struct FPropertyChangedChainEvent& PropertyChangedEvent );

I’ll be waiting for your response.

Best,

Joan

Hi Joan,

Thanks for testing these options. I realise that my case is a little bit more specific so there are a couple of changes to make before you can reproduce it.

  • First, in your Object example make UMyOuterThing inherit from UDataAsset. This should also be Blueprintable.
  • Then in your project, create a blueprint inheriting from UMyOuterThing
  • Reference this blueprint from your main data asset that inherits from UMyConfigAsset
  • Change details in your new blueprint and observe that the variables do not also change in your base config asset.

Hi [mention removed],

I’ve just made these changes, and in my project instanced properties do get saved and serialized correctly. I think there might be a miscommunication in terminology, which is preventing me from reproducing the issue exactly as you described.

First of all, you mentioned:

“If the first instanced object has its own instanced property, I do not see the updates in the data asset any more.”

Then you mentioned that UMyOuterThing should inherit from UDataAsset and be Blueprintable. When you say “Instanced Object,” I understood that we’re talking about a UPROPERTY(Instanced), which creates the object together with the Data Asset and stores/serializes it inside the same package. When you mentioned “Instanced properties,” do you actually mean a Blueprint .uasset created from a base class, or an inline Instanced UPROPERTY?

In your last message, you also wrote:

“Change details in your new blueprint and observe that the variables do not also change in your base config asset.”

By this, do you mean that when modifying the Blueprint properties inside the Blueprint’s Details Panel, you expect to see those changes reflected automatically in the Data Asset (UMyConfigAsset in my case) that references that Blueprint? Is that correct?

If possible, would you be able to adjust my code sample or provide a minimal example that matches your exact use case? If we can reproduce the case on our side, we can look for a potential workaround for your project. With all of this we can report it and escalate it directly to Epic if it is an actual engine bug.

Best,

Joan

Hi Joan,

Sorry for the confusion. Here’s my updated code.

// Fill out your copyright notice in the Description page of Project Settings.
 
#pragma once
 
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "MyConfigAsset.generated.h"
 
UCLASS(EditInlineNew, DefaultToInstanced, BlueprintType)
class UMyInnerSettings : public UObject
{
	GENERATED_BODY()
     
public:
	UPROPERTY(EditAnywhere, Category="Inner")
	int32 Number = 0;
     
	UPROPERTY(EditAnywhere, Category="Inner")
	FString Label = TEXT("Default");
};
     
UCLASS(EditInlineNew, DefaultToInstanced, BlueprintType, Blueprintable)
class UMyOuterThing : public UDataAsset
{
	GENERATED_BODY()
     
public:
	// Nested instanced object (the troublemaker)
	UPROPERTY(EditAnywhere, Instanced, Category="Outer")
	UMyInnerSettings* Settings = nullptr;
     
	// A plain field so you can see "outer-level" edits do save fine
	UPROPERTY(EditAnywhere, Category="Outer")
	float Multiplier = 1.0f;
};
     
UCLASS(BlueprintType, Blueprintable)
class UMyConfigAsset : public UDataAsset
{
	GENERATED_BODY()
     
public:
	// Top-level instanced object (generally reliable)
	UPROPERTY(EditAnywhere, Instanced, Category="Config", meta=(DisplayName="Outer Thing"))
	UMyOuterThing* Outer = nullptr;
     
	// Mirrors a value so you can see whether the asset serialized
	UPROPERTY(EditAnywhere, Category="Config")
	int32 MirrorOfInnerNumber = -1;
};

In my project I created a new Data Asset that inherits from MyConfigAsset. I’ve also created a Blueprint that inherits from MyOuterThing called BP_Outer.

In my DataAsset, I set my OuterThing value to BP_Outer. If you now expand the inline panel in your DataAsset, you should be able to see the values that are defined in BP_Outer.

In BP_Outer, set settings to MyInnerSettings and compile/save. Observe that it changes as expected in your DataAsset.

In BP_Outer, now set the settings to None and compile/save. Observe that it does not change in your data asset until you press your reset to default value button in the Data Asset.

Another repro can be seen if you set up reset both assets to defaults and then point the Data Asset at BP_Outer again.

Set the settings value in BP_Outer to MyInnerSettings. This should show in your Data Asset.

Set the inline Label value in BP_Outer to “Test”. Observe that this updates in your Data Asset as expected.

Now click on the settings dropdown in BP_Outer again and click on MyInnerSettings again. This will change all inline values to default. However, your Data Asset will not have updated. It seems that at this point, the values diverge and your Data Asset will still show Test while your blueprint is showing Default. To align these again, you’ll need to reset your values in the Data Asset to defaults.

Hopefully those cases are something you can reproduce on your side too!

Hi Kieran,

After digging into the engine code, this appears to be an engine bug. When debugging the internal variables of the classes, it looks like the InnerSettings objects in the DataAsset and the Blueprint are two different instances, not connected to each other.

In my test scenario, changing the Label updates correctly as you mentioned, but changing the Number does not, the values are not updating consistently. Some values modified in the Blueprint update in the DataAsset, but others don’t, and changes made in the DataAsset are not reflected in the Blueprint either. I don’t think this behavior was intended by Epic’s developers, as it’s inconsistent.

I’ll report this to Epic so the team responsible for this system can take a closer look. Thanks for providing the detailed steps. With your repro, it was straightforward to reproduce the issue. I’ll update you once I get a response.

If this is blocking your workflow, we can look into an engine-level workaround in the meantime.

Best regards,

Joan

Hi Joan,

Thanks a lot for confirming and sending the issue onwards. I’m glad it’s not just something on my side!

This issue was found through a bug report via a plugin I’m working on so ideally I’d be able to find a temp fix. Best case would be a fix in the plugin so that they don’t need any additional changes but if it has to be an engine change until the proper fix comes through, that may be enough for now.

Perfect Kieran!

We will try to look for a solution that does not involve changing the engine. Will give you a response in the following days.

Best,

Joan

Hi [mention removed],

After digging a bit deeper into this, the behavior we’re seeing actually matches how Unreal handles archetypes and instanced properties.

In this setup, BP_Outer is a class/archetype, not an instance. When we assign it to the variable inside DA_MyConfigAsset (the UMyConfigAsset DataAsset), the engine creates a new instance whose class is BP_Outer_C, initialized from that Blueprint’s default values (its CDO).

Because that property is marked Instanced (and the type uses DefaultToInstanced), the DataAsset now owns its own copy of the settings rather than referencing BP_Outer directly.

When you edit values in the DataAsset, you’re modifying the DataAsset’s owned instance, not the Blueprint asset. Those changes do not and cannot propagate back into BP_Outer, the Blueprint remains the archetype, and the object inside the DataAsset is just an instance created from it.

On the other hand, changing variables on the Blueprint asset (BP_Outer) will propagate down to the DataAsset as long as that property hasn’t been overridden in the instance. As soon as you modify a given property in the DataAsset, Unreal considers that property overridden relative to the archetype, and further changes made in the Blueprint’s defaults for that property will stop updating the instance.

This is the same inheritance/override behavior you see between parent and child Blueprints or between a Blueprint and its placed instances in a level.

If you want changes in the DataAsset to also apply back to the Blueprint, that would mean updating the archetype’s defaults directly and reconstructing existing instances derived from BP_Outer. That’s not something the engine does automatically and isn’t really how the system is designed to work.

For your use case, the most robust approach is to put the shared configuration into a dedicated DataAsset and reference that asset from both the Blueprint and UMyConfigAsset. This way, both consumers point to the same underlying object, and any changes to that shared DataAsset are reflected consistently in both places.

It’s also worth noting that any property marked as Instanced is always created inline and owned by its outer object. By design, those instanced subobjects do not share state with other objects of the same type; they’re intentionally unique per owner.

From what I’ve been able to see, there have already been multiple reports regarding the behavior of the Instanced keyword. Similar issues have been encountered by other developers, but most of these reports have been backlogged or marked as “Won’t Fix” by Epic, as the current behavior aligns with the intended design of the property system.

This topic often causes a lot of confusion across different areas of the engine, as the distinction between archetype defaults, instanced subobjects, and shared assets isn’t always intuitive at first glance.

Best,

Joan

The only issue that I see with sharing data with the DataAsset is that it won’t get serialized within the same object, but if you want data to be the same in all instances, it does make sense that they point to the same data source.