UObject-derived Class as UPROPERTY - How can I get this to serialize/save?

Background:
So, I have an editor extension for generating maps for my game. There are buttons that effectively add components to my Map actor. These added components are derived from UStaticMeshComponent, but they have a UPROPERTY added that lets me store some info about the room the static mesh represents. All well and good so far.

However, when I save the map and reload, all the components come back…but the UPROPERTY UObjects do NOT. Apparently, UPROPERTYs whose types are derivatives of UObject are not serialized with the component? Is that correct?

Question:
Is there a UPROPERTY specifier or UPROPERTY metadata specifier that I can use to force the UObject derived class to serialize with the other UPROPERTYs?

Additional Info:
This behavior is specifically happening when I’m using functions IN THE EDITOR, i.e., they are blueprint functions (which wrap c++ functions) marked as “Call in editor.” The “game” is not actually running, its all in editor.

I did create a basic from-empty-project sample that shows this in action, but the simplest form I could create is still a dozen c++ files. I verified that UStructs serialize just fine, but UObject-derived classes do not. I can provide the source code if needed, I just don’t have it hosted on any publicly available repos at present.

I’m GUESSING maybe I need to write a custom serializer for the component that has the UObject-derived property? I don’t really get that, though, as it work fine with structs. (I don’t really want a struct here, though…I want to save an object.)

Its hard to tell what you are doing without seeing code. But the question has 2 parts. Reflection, and instantiation:

UPROPERTY() means its reflected. Which is required to serialize. Which is required to use in BP’s, and save, load from disk, or serialize from a umap.

In order to create the object, it must be instantiated, then it can be serialized into.

IE: the object pointed to MUST exist before knowing what it contains. You MUST create the object EVERY time the class gets instantiated.

CreateDefaultSubobject<UMyObject> () needs to be called from the constructor.

UPROPERTY(Transient)  //&lt;-- Transient means this WILL NOT get SAVED or LOADED.
uint64  NPC_Class;                 

UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "SOP")
USOPScript* SopScript;   //&lt;--- this gets Default Constructed, and serialized to disk, or into levels.

UPROPERTY(Transient)
USOPState* SopState;   //&lt;--- this holds state information, and does not get serialized to disk or into levels.  But does get serialized for access by BP's.

And the code?

Sounds like you want the behavior of “instanced” and “editInlineNew” specifiers.

Created a GitHub repository with sample project demonstrating issue.

In the sample code, I’ve got a BP_SaveActor that has several editor functions. There is a BP_SaveActor in the default level. If you select that actor, and click “Clear Generated Components”, then click, “Create Component”, the BPSaveActor creates a new UTestComponent, and a cube will appear. Then you can click List Components, and you’ll see some debug text onscreen. It SHOULD show that there are values for BOTH debug lines. (one line is for a UStruct that is a UPROPERTY of the UTestComponent, the other line is for a UDataObject (UObject derivative)).

SO, right now, clicking List Components works fine.

Now, save the project, close UE4 editor, re-open the project in UE4 editor, and select the BP_SaveActor. Click “List Components”. You’ll see that the line for the UStruct gives you data because it serialized correctly. The line for the UDataObject is all zeros.

I DID add a “CreateDefaultSubobject” because that was missing, but it made no difference to functionality. I also tried adding “Instanced” to the UPROPERTY and “EditInlineNew” the UClass, and again, no difference.

Any guidance on what I’m missing? Am I just misunderstanding what SHOULD happen here?

So…Additional Info. At least in the simplified sample I posted, I CAN get the functionality I want by changing this:


UTestComponent::UTestComponent(const FObjectInitializer& ObjectInitializer) {
UE_LOG(LogTemp, Error, TEXT("%s Constructor (Line %s)"), *CPP_CLASS, *CPP_LINE);
DataObject = ObjectInitializer.CreateDefaultSubobject<UDataObject>(this, TEXT("DataObject"));
}

to THIS:


UTestComponent::UTestComponent(const FObjectInitializer& ObjectInitializer) {
UE_LOG(LogTemp, Error, TEXT("%s Constructor (Line %s)"), *CPP_CLASS, *CPP_LINE);
ObjectInitializer.CreateDefaultSubobject<UDataObject>(this, TEXT("DataObject"));
}

in the constructor of the UTestComponent class. Basically, if I throw away the returned pointer from CreateDefaultSubobject, it seems to work.

Which…makes no sense to me. I guess I just really don’t understand what CreateDefaultSubobject is doing. Can anyone shed some light on what is going on?