UObjects nested in natively created components are initilized incorrectly

Sometimes it is useful to aggregate component’s behavior in UObject-derived classes. To do this (in c++) you need a simple setup:

  1. Declare your class UMyObject derived from UObject.
  2. Set UCLASS flag “DefaultToInstanced” (to avoid setting UPROPERTY “Instanced” flag whenever an object of this class is referenced).
  3. Create any component UMyComponent.
  4. Add UPROPERTY(VisibleAnywhere) UMyObject* MyObject into UMyComponent.
  5. Instatiate MyObject by calling CreateDefaultSubobject() in the UMyComponent constructor.
  6. Instantiate UMyComponent as default subobject at any custom actor. It’s essential to do it in c++ - it’s an actual problem!

At this point you might want to add some properties directly to the nested UMyObject and allow their modification from BPs. If the setup is correct - such properties would appear among the UMyComponent properties. Modifying them, saving BP asset, closing the editor, opening again - all work well - modifications are serialized and loaded.

However when running the actual game UMyObject properties are initialized to CDO values. Despite that an instance of the owning UMyComponent is properly intialized.

At the same time adding UMyComponent to the same actor through BPs will work absolutely fine - all UMyObject properties are correctly initialized in both editor and game.


Why it behaves in such weird way?

The potential reason for this is covered inside FObjectInstancingGraph::GetInstancedSubobject(…):

// Don't search for the existing subobjects on Blueprint-generated classes. What we'll find is a subobject
// created by the constructor which may not have all of its fields initialized to the correct value (which
// should be coming from a blueprint).
// NOTE: Since this function is called ONLY for Blueprint-generated classes, we may as well delete this 'if'.
if (!SubobjectOuter->GetClass()->HasAnyClassFlags(CLASS_CompiledFromBlueprint))
{
	InstancedSubobject = StaticFindObjectFast(nullptr, SubobjectOuter, SubobjectName);
}

An Outer for our subobject UMyObject is UMyComponent. If it is created natively - the ‘if statement’ == true and StaticFindObjectFast will find CDO which is not actually initialized with the values set in BP. On the other hand if UMyComponent is added from BPs the ‘if statement’ == false and the next code will be processed:

if (!InstancedSubobject)
{
	// finally, create the component instance
	FStaticConstructObjectParameters Params(SourceSubobject->GetClass());
	Params.Outer = SubobjectOuter;
	Params.Name = SubobjectName;
	Params.SetFlags = SubobjectOuter->GetMaskedFlags(RF_PropagateToSubObjects);
	Params.Template = SourceSubobject;
	Params.bCopyTransientsFromClassDefaults = true;
	Params.InstanceGraph = this;
	InstancedSubobject = StaticConstructObject_Internal(Params);
}

Here the SourceSubobject is an instance of UMyObject created during the deserialization (loading) process and contains the desired data.


Is it intended behavior? Are there any uproperty/uclass flags that solve this issue?

Finally I have found an unresolved bug ticket for this issue: Unreal Engine Issues and Bug Tracker (UE-40878)
Please vote for it if you experience the same issues.

1 Like