UObject Constructor, PostInitProperties and PostLoad

Unlike the common c++ constructor, Unreal Engine 4 has a lot of things going on while it creates or loads an object. And in this post I am going to explore the three functions related to object creation: Constructor, PostInitProperties, and PostLoad.

This post is a part of my Unreal Engine study, the goal is not to modify or tweak the engine but to make me better at deciding where I should write a particular object initialization code in the game. I assume you already have at least a bit of understanding and experience in writing Unreal Engine c++ codes and have generally known the unreal engine tool chain (UHT and UBT).

Change of Paradigm

UObject is the base of almost all of Unreal Engine objects, including UProperties, UStruct, UClass, UActor, etc. When writing a c++ class sometime we need to initialize some properties that we have made, for example:

class MyClass
{
	int32 X;
	int32 Y;
public:
	CFoo( int32 InX, int32 InY )
    	: X(InX)
    	, Y(InY)
	{}
};

And then we do the following to create an object of that class:

MyClass A = new MyClass( 10, 10 );

It is pretty standard, and we probably use it all the time in c++ but alas we can not do it like that for UObject class, it is inconvenient at first, especially when you’re used to it before you are using Unreal Engine, but all we need to do is to change our paradigm, Unreal Engine has so many cool features such as object replication, reflection, object garbage collection, etc. You only need to adapt to the special way in which Unreal Engine does its things, the trade off between convenience and benefits are very significant, especially if we broaden our view to the whole game production, it empowers the whole team, not just programmers, we can implement cool game features that are easy to tweak, we can create our own game system based on what Unreal Engine has provided and create a custom editor for it, and many more. If you do not change your paradigm and insist to use the way you are convenient with before you use Unreal Engine then you will have a hard time in your game development.

One of my friend insisted that he wanted to use a full non-unreal engine c++ to create item and inventory system for his game just because he had done something similar in his previous game and he did not want to change his way of doing things, what happened next was he spent weeks implementing it, I admit it was a solid system from native c++ perspective but it was so hard to integrate it to Unreal Editor, he needed to make his own serialization code, he needed to make his own item editor from scratch, he needed to expose his non-unreal objects to actors in the world, it was just too hard to do and he gave up midway, he missed the point of using Unreal Engine to help us game programmers to implements our features easily.

All we need to do is to accept it as it is and just climb the steep learning curve of Unreal Engine c++ programming and we will reap the great benefit of increased productivity and efficiency later.

Methodology

Now back to the topic, this particular blog post is my attempt to understand what UObject Constructor, PostInitProperties, and PostLoad are. The method I am using is to write object information logs whenever these functions are called by Unreal Engine. The information are: Object Path, Object Flags, Class Flags and the value of a custom integer property: Foo.

All Unreal object has a unique path in the form of a string, for example:

/Game/MyGame/MyAsset.MyAsset.ASubObjectOfMyAsset.AnotherObject

If you split the path using dot as delimiter you will get:

1. /Game/MyGame/MyAsset
2. MyAsset
3. ASubObjectOfMyAsset
4. AnotherObject

Each of them represents different objects where the later object is contained within the previous object, in Unreal Engine term: the later object has the previous object as its outer. The outer-most object is always a special object called Package, in this case it is /Game/MyGame/MyAsset, and all Unreal Engine object except for Package always directly or indirectly contained within a Package.

Object flags is a bit flags that tells us the state of the object, for example if it has RF_Public then this object is visible outside of its package.

Class flags is a bit flags of the object’s class, for example, if it has CLASS_Abstract then the class can not be instantiated. Class itself is a UObject that has UStruct as its parent, but I am not going to delve into it this time as it deserves its own study.

For the purpose of the test I created an object class, UTestObject :

UCLASS(Blueprintable)
class PACZ_API UTestObject : public UDataAsset
{
    GENERATED_BODY()
public:

	/** Custom Property, Foo. */
	UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = "Test" )
	int32 Foo = 100;

public:

	/** Constructor. */
	UTestObject(const FObjectInitializer& ObjectInitializer);

	/** PostInitProperties override. */
	virtual void PostInitProperties() override;

	/** PostLoad override. */
	virtual void PostLoad() override;
};

I put my logging codes in all of the three functions: UTestObject (the constructor), PostInitProperties and PostLoad; I will then examine the logs that are written whenever these functions are called.

Pacz is the primary game module that I use to do the test, thus PACZ_API is the API keyword for this module, unless you are certain that you want to keep this class private within its own module then it is a good practice to add the API keyword (or MinimalAPI class flag) to your class, because you never know whether you want to expose it later along the development, I will not discuss this any further because module management is a big subject that has already been clearly documented in Unreal Engine learning resources.

ClassDefaultObject

After I have written my UTestObject in c++, I started up the editor, and it triggered the first construction of my test object, the logs are as follows:

[Constructor]
     Object Path  = /Script/Pacz.Default__TestObject
     Object Flags = Public ClassDefaultObject ArchetypeObject
     Class Flags  = Native RequiredAPI Intrinsic Constructed
     Foo          = 100
[PostInitProperties]
     Object Path  = /Script/Pacz.Default__TestObject
     Object Flags = Public ClassDefaultObject ArchetypeObject
     Class Flags  = Native RequiredAPI Intrinsic Constructed
     Foo          = 100

The object constructed by Unreal Engine was the ClassDefaultObject, every class has at least one default object, it is an ArchetypeObject which means that it is an object that can act as a template for another object instantiated from this class.

Now please examine the package of this object, it is in /Script/(ModuleName), in my case it is /Script/Pacz, your native unreal objects (including classes) in a certain module will all be contained in this package, it is populated by Unreal Header Tool (UHT) whenever you compile your c++ code. If you have another game module in your project, for example MyGameEditor, then all native unreal objects in that module will resides in /Script/MyGameEditor. Please note that all c++ codes that are not marked by special UHT macros (e.g: UENUM, UCLASS, UFUNCTION etc) will not be visible to Unreal Engine, it will not even know that these functions, classes, or properties even exist in the first place.

The object flag Public (RF_Public), means that this object is visible outside of its package, an example of non public object is a sub object, you need to actually get the instance of its outer and then access the object. Every object you can see or use in the editor without opening a special editor for a certain object (e.g Blueprint Editor, UMG Editor, etc) is Public.

Now lets see the Class Flags, the class is marked as Native, which means it’s from native c++ code instead of from Blueprint (Blueprint generates non-native object class), it is also marked as RequiredAPI, this is the result of adding PACS_API when I wrote the class in c++, if I was to omit the API keyword then it will not be marked as such.

The last class flag is Constructed, all native c++ object classes will be marked as constructed, only blueprint generated classes have the possibilities of not being marked with this flag when those classes have not been constructed by the blueprint construction system.

The last line in the log shows the value of Foo, the value is 100 because this is what I have specified in c++, we will later drr how it will change when we create an asset of this class.

If you are meticulous enough you should have already noticed that PostLoad was not called in this phase, that’s because native objects does not need to be loaded, or we can say that they have already been loaded when the dynamic library in which they reside is loaded by Module Manager.

UTestObject as Asset

Because UTestObject is derived from UDataAsset, it can be instantiated as an asset in the editor, so I go ahead and make an asset of it and I named it MyTestObject and this is the log:

[Constructor]
     Object Path  = /Game/MyTestObject.MyTestObject
     Object Flags = Public Standalone Transactional
     Class Flags  = Native RequiredAPI TokenStreamAssembled Intrinsic Constructed
     Foo          = 100
[PostInitProperties]
     Object Path  = /Game/MyTestObject.MyTestObject
     Object Flags = Public Standalone Transactional
     Class Flags  = Native RequiredAPI TokenStreamAssembled Intrinsic Constructed
     Foo          = 100

What has happened here was Unreal Engine created a new package, /Game/MyTestObject, and then it created a new object of class UTestObject, and then the class default object was copied to this new object, it is the reason why the value of Foo for this new object is 100.

Let’s look at the object flags, it’s not the same as the flags of class default object, because they are different, this is an asset object, not an archetype object, this is why it does not have both ClassDefaultObject and Archetype flag anymore.

There are two new flags in this object: Standalone and Transactional.

Standalone means that this object will not be discarded even if it is not referenced by another object, you have probably known that Object will be discarded (or garbage collected) by unreal engine if it is not strongly referenced or it is not rooted (rooting an object is not within the scope of this post so I will not discuss it here), and from this test we also know that objects that are flagged as standalone will not be garbage collected, but the caveat here is that they are not discarded as long as the packages they’re in are loaded, if for some reason the packages are unloaded, these objects will be elligible to be garbage collected;

Transactional means that almost all changes to the value of the properties of this object are recorded and can be reverted, which means we can undo or redo the changes in the Editor.

In the log, you can see that the UTestObject class gains a new flag, TokenStreamAssembled, this means that the token stream of this class has been assembled and ready to be used by others (Kismet blueprint nodes, details panel, property widgets, etc), Unreal Engine assembles the token stream before hand because the user needs it to edit the asset. I am not particularly sure about this but I assume this is what had happened based on my limited understanding of Unreal Engine.

Editing Foo

I opened up the asset by double clicking it, it opened up an object editor, and then I changes the value of Foo to 25 and save the asset.

There is nothing printed in the log, which means none of the three functions are called, but what happened here was I directly edited the property Foo and the package in which the object is contained is marked as dirty (it is displayed as asterisk character at the bottom left of the asset’s thumbnail), and then when I saved it the asset object was serialized to disk and the asterisk character was gone (because it was not dirty anymore).

Post Load

So, when does the PostLoad function gets called by Unreal Engine? for this we need to restart the editor in order to force the asset to be reloaded (there are other ways to do this but this is the simplest one and the test project is small enough that restarting it only takes a few seconds).

After I restarted the editor, there is no PostLoad for the new asset, why? because Unreal Engine should not do that, all assets are in the form of meta data (FDataAsset) when the editor starts, the actual assets are loaded only when they are actually used, let’s imagine a project that has gigabytes of assets, if Unreal were to load them when the editor starts, they will take tremendous amount of time and memory space and I am not sure if the PC we’re using is able to handle such huge amount of data.

To trigger PostLoad, we need to use the asset, so I just opened it in the editor and this is the log:

[Constructor]
     Object Path  = /Game/MyTestObject.MyTestObject
     Object Flags = Public Standalone Transactional NeedLoad NeedPostLoad NeedPostLoadSubObjects WasLoaded
     Class Flags  = Native RequiredAPI TokenStreamAssembled Intrinsic Constructed
     Foo          = 100
[PostInitProperties]
     Object Path  = /Game/MyTestObject.MyTestObject
     Object Flags = Public Standalone Transactional NeedLoad NeedPostLoad NeedPostLoadSubObjects WasLoaded
     Class Flags  = Native RequiredAPI TokenStreamAssembled Intrinsic Constructed
     Foo          = 100
[PostLoad]
     Object Path  = /Game/MyTestObject.MyTestObject
     Object Flags = Public Standalone Transactional WasLoaded LoadCompleted
     Class Flags  = Native RequiredAPI TokenStreamAssembled Intrinsic Constructed
     Foo          = 25

As you can see here, in PostInitProperties, the value of Foo is still the same as the one from ClassDefaultObject, 100, only after the object has been loaded will it change to the value we have previously entered, 25. You have probably known that we can revert the value of a certain properties by clicking the yellow rollback arrow at the end of the property value, reverting a value means that the value is reverted back to the archetype object value.

Conclusion

This is the end of the simple test that I have conducted, there are many interesting things that I have learned during the test but I did not write in this post because I want to keep it short (because it’s just a blog post, not a book).

I also realized that I haven’t explained the difference between Constructor and PostInitProperties, although they looked the same but they are actually two very different things, to put it simply in Constructor the object is still an abstract entity, it can not see the “world” yet, it’s still in incubation, while in PostInitProperties the object has been born, it has gotten out of its womb and ready to go on an adventure. There are a lot of things you can do in the constructor that can not be done in PostInitProperties and vice versa.

I have actually done a lot of other similar test for many kinds of objects, the most interesting one is when I examined an actor blueprint using this method, I learned how both the skeleton object and the generated class default object of the blueprint were constructed and loaded, I also learned the difference between object flags and class flags for different object instances in the blueprint. What happened under the hood when the actor blueprint was spawned into the world really helped me to both understand more about Unreal Engine and helped me implemented a particular feature in the game that i am currently making.

Some of you may think that it is unnecessary to know these kind of things and to just use blueprint for everything, it’s probably true for small game projects or a project that only uses the existing Gameplay classes that Unreal Engine has provided (they are mostly related to multiplayer game using a set of built-in character archetypes) but what if you wanted to make an original gameplay that is different from what has been provided by Unreal Engine? Let’s say you are creating something like Civilization or Skyrim, what will you do then? you will inevitably need to know this stuff, and it’s not an easy thing to learn, at least for me, learning it as early as possible is the best way to do to prepare for the problems we are going to face later in our project (or future projects).

Thank you for reading this post and I will be very happy and grateful if you can spot any errors in my understanding of Unreal Engine and tell me all about it in the comment section or e-mail.

Published by

Fathurahman

I am a game programmer interested in many aspects of game programming especially tools development and User Interface. I am currently using Unreal Engine 5 for both work and off-work projects. I love to do figure drawing and digital painting in my free time.

4 thoughts on “UObject Constructor, PostInitProperties and PostLoad”

  1. Just wanted to say.
    Thanks!
    Have been banging my head off, and just realized that PostInitProperties doens’t have the real non CDO values.
    This was a pretty solid confirmation.

    Keep up the good work
    (even 3 years later)

    Like

  2. Very interesting article, thank you. I know it’s been 5 years, but what you described is still very much useful in 4.27 and, I suspect, in UE5 too.

    Like

Leave a comment