top of page
  • Writer's pictureEngineer Luna

UMG Pre Construct - Break Down & Best Practices

When we first open up the UMG editor graph, the first thing we are presented with is the Event Pre Construct, Event Construct and Tick. It feels almost natural that when we have the need to run logic on begin play of a UMG widget blueprint, this is the right place to do it. A large portion of the time this would be inefficient and possibly dangerous, let's talk about why this is.

If you know about slate and UMG construction, you can skip the next part.

So what is actually going on?

UMG widgets are essential UObject wrappers around slate. This brings a few benefits like allowing none engineers to build and tweak UI, editor reflection, fast editor iteration of UI etc

An easy way to build a mental model of slate and UMG is to image a puppet(Slate) being controlled by a puppeteer(UMG).

It's much easier to ask a puppeteer to make the puppet perform actions than for most people to attempt to pull the strings them self.

Event Pre Construct

This is called by before slate construction of the underline widgets, this allows for cosmetic & widget setup. This function is called both in editor and in game.

Event Construct

Called after underline slate widgets are constructed, this call be called multiple times if the underline hierarchy changes.

What alternatives do we have?

In most cases, the On Initialized event can be used, this is called after all construction is finished and is only ever called once. By default, this is not on the graph, and you need to search for it.

So what can go wrong?

Let's go over some examples of things that can go wrong.

Data Flow

Setup Image

What are we trying to do?

We have two int variables Foo-Set and Bar-Increment, both have a default value of 0. When the events Pre Construct, Construct and on Initialized run we execute the following logic. Set Foo-Set to a value.

  • Pre Construct - 1

  • Construct - 2

  • Initialized - 3

Increment Bar-Increment by 1.

Print both values and the event name. What we expect to happen? We expect these output results:

  • Pre Construct - Foo-Set 1 | Foo-Increment 1

  • Construct - Foo-Set 2 | Foo-Increment 2

  • Initialized - Foo-Set 3 | Foo-Increment 3 // If we are in game

What is actually happening?

Let's have a look at some output results.

Editor Compile:

  • Pre Construct - Foo-Set 1 | Foo-Increment 1

  • Pre Construct - Foo-Set 1 | Foo-Increment 2

Editor Save - After Compile:

  • Pre Construct - Foo-Set 1 | Foo-Increment 3

  • Pre Construct - Foo-Set 1 | Foo-Increment 4

In Game:

  • Initialized - Foo-Set 3 | Foo-Increment 1

  • Pre Construct - Foo-Set 1 | Foo-Increment 2

  • Construct - Foo-Set 2 | Foo-Increment 3

What the heck is going on? When it comes to the editor preview, we only run the pre construct event. This will run twice on a blank widget. If I remember correctly, this is due to widget designer holding two instances of the UMG widget, the first instance is a protected compiled version you are unable to change directly. The second is the one is a copy of this widget that you are able to edit using the UMG designer window.

This is the reason you are unable to touch any widgets added during pre-construct, because they are not pushed to the forward facing widget, meaning they never hit the widget tree. This is why they also do not respect widget bounds.

When it comes to the permeance of local data while in the editor, this can get a little funky. If you compile a widget blueprint it is recreated under the hood and CDO are reset, however when you press save this is not the case. This can result in data being manipulated multiple times.

At first clear it would seem that runtime execution order would be as follows

  • Pre Construct

  • Construct

  • Initialized

But this is not the case and can be a misleading, let's break it down

  • Initialized - Called when the widget has finished initializing

  • Pre Construct - Called before underline slate widgets are constructed

  • Construct - Called after underline slate widgets are constructed

This is something to keep in mind when it comes to runtime logic vs editor. A good way to think of this is back to the mental model we talked about earlier. Before a puppet can be setup and controlled, its puppeteer must be ready.

A few good practice notes

When creating UMG widgets with dynamically added elements during design, it's crucial to understand that nesting these widgets within others incurs a compile time cost for every individual instance. This cost is not incurred just once per widget, but for each separate instance of the widget.

Large amounts of hierarchy nesting that include pre construct logic can add up, this is even worse when you are handling generated elements. It's advisable to steer clear of loading assets asynchronously during the pre-construct phase. This practice can lead to significant problems with memory management and might interfere with other systems, such as the asset auditor.

60 views0 comments


bottom of page