When creating more and more complex and flexible UserWidget Blueprints in Unreal, one problem is that their appearance in the editor can be very different to their final appearance in-game.
Inventory Panel Example
Imagine for example that you want to create a generic inventory display widget. Something we can use throughout our UI, wherever we need to show the contents of something.
- A title, describing the category of items being shown
- A grid of X columns by Y rows.
UserWidgetto represent a single item, that we will show in our grid.
By default if you create this as a
UUserWidget subclass in Blueprints,
in the editor this could look like an empty widget,
with the label showing its default placeholder text.
Notice we could add a public variable to define the title, number of rows and number of columns that the widget will be set up with in-game, but the appearance in-editor will be nearly empty.
On the other hand in-game, the size and appearance of this widget will change completely. Its label will be updated, and it will be filled by inventory item widgets for displaying each item.
We can solve this with C++, and let the widget update in the editor. The
key to this is the
SynchronizeProperties function in
UUserWidget. In the
editor it is called every time that a property is modified or the Blueprint is
compiled. We can override it and inside use it to initialize our user widget
in the same way it will be set up in-game.
- Our properties are marked as
EditAnywhere, allowing us to set up defaults in the Blueprint class definition, and then override them in instances of the UserWidget.
- We made the
FName. This is because we want our text to be localizable. See the page on Unreal UIs and Localization for more information.
- We use the
BindWidgetmeta property to let us access the widgets we will create in our Blueprint subclass. This is explained in Connecting C++ to UMG Blueprints with BindWidget
With the code in-place we need to create a Blueprint subclasses it, or change our existing widget Blueprint to be a subclass of it.
As we discussed before with the BindWidget meta
property, in order for the properties to be
set up correctly you must create widgets with the same type and name as your
BindWidget properties, and mark them as Variables. You can see now why
creating C++ base classes is so useful for UI development.
You also need to create a widget Blueprint to use as your inventory item widget. Once this is compiled you should be able to select it from the drop-down menu next to ItemWidgetClass.
With this done, as you change the values of ItemPanelWidget, you should see its appearance change in the editor. Changing the text in LabelText should update what is shown in the label box, and changing Rows and Columns should change how the widgets are shown.
We saw how
SynchronizeProperties is invaluable for creating widgets that update
their appearance dynamically in the editor. This can be used to create far more
user-friendly widgets, whose in-editor appearance more closely represents how
they will be shown in-game.
Update: Event Pre Construct
As of Unreal 4.16, it is now possible to perform some of the functionality of
SynchronizeProperties from Blueprints by using the new Event Pre Construct
node. This is called in both the editor and the game and lets you set up your
widget's appearance in the editor based on its settings.
However I still think in the long-term it is worth putting as much of your logic into C++.