Introduction to UIs in Unreal

Welcome to the first in a series of tutorials on creating UIs in Unreal. Most other tutorials I’ve seen online only cover basic UI creation using Blueprints and UMG, however this series is focussed on creating professional-quality UIs in Unreal using C++ and Blueprints.

Unreal UI Introduction Series

  1. An Introduction to Making UIs in Unreal
  2. Making UIs in Unreal with C++: Basics
    1. Creating C++-based UserWidgets
    2. Creating New UMG Widgets
    3. Creating New Slate Widgets (Coming soon)

The tutorial series can be broken down into three major sections:

  1. Cover the basics of creating UIs in the editor, using UMG and Blueprints.
  2. Transition to implementing our UI logic in C++, and tying it to visuals in UMG.
  3. Learning how to create new types of base widgets in pure C++.

Prerequisites

Before starting this series you should:

  • Have experience with the basics of Unreal - the Content Browser, creating and scripting Blueprints, UObjects, things covered by basic UE4 tutorials.
  • Be competent with C++. You don’t have to be an expert, but you should understand inheritance, pointers, references, etc.
  • Be comfortable with creating C++ classes and using them in Unreal. There are many great tutorials that cover creating components and other code in C++.
  • Have access to the Unreal engine source code from Github.
  • Have a passion for creating UIs :)

Let’s Get Started

The first thing we will be dealing with is UMG, the name given to Unreal 4’s latest UI system. We will first get used to using it from within the editor, and learn what its features and limitations are.

In order to get started with UMG, the first thing you should do is create a space that lets you experiment with the UI tools available.

In the case of level design this would be making a new Map asset in Unreal. In the case of UMG, you need to create a UserWidget asset. We’ll talk about what UserWidgets are in detail below. But for now just right-click in the Content Browser, choose UserWidget from the pop-up menu and call it whatever you want.

At first Unreal’s UMG system can be a bit daunting. There are a large number of widgets, a large number of options on each one, and learning how they all affect each other is quite a steep learning curve. But don’t panic, this stuff will make sense the more you use it. I promise :)

The editor view on UserWidgets is split into two tabs, shown in the top-right. The Designer view and the Graph view. They are two views onto the same widget, one is its visual aspect, the other its logic.

  • The Designer view is where you set up how your UserWidget will look when it is created. You can add new widgets, lay them out, change their default properties.
  • The Graph view is where you set up your Blueprint logic and control how the widget will behave in-game.

Editing UserWidgets in the editor (Labelled screenshot of UI)

Designer View

Designer view of the editor
▲ Designer view of the editor

The screen presented to you is composed of a few parts:

  • Palette: Lists all the widgets available to you
  • Hierarchy: The tree view of your UserWidget, with the UserWidget’s root at the very top. You can click on any of the widgets to select them.
    • Widgets in bold are “Is Variable” widgets. These are available in the Graph view.
  • Viewport: The selected widget is shown with a green border.
  • Details: Shows the properties available on the curently-selected widget.
  • Animations: Lists animations within the current UserWidget
    • Timeline: Shows the timeline of events for the currently-selected animation

We will cover the details of some of these sections later, for now let’s move on to explaining more about how UMG documents work.

Graph View

Graph view of the editor
▲ Graph view of the editor

Nesting and Slots

The first thing to understand is that the “document” in UMG is a tree of nested widgets. Some widgets cannot contain any children, some can contain a single child, and some can contain many children.

  • no children: Image, TextBlock
  • one child: UserWidget root, Border, NamedSlot
  • many children: CanvasPanel, Overlay, WidgetSwitcher

Slots

When a widget is put inside another, we can customise how it behaves inside its parent through its Slot property. In the editor it’s the first set of variables shown in the the property view.

On the left, Image_1 is inside a CanvasPanel. On the right it is inside an OverlayPanel.
▲ On the left, Image_1 is inside a CanvasPanel. On the right it is inside an OverlayPanel.

Notice in the screenshot that our Image widget’s Slot properties are different depending on whether it’s inside an Overlay or a Canvas widget.

In the Overlay Slot property we can set how the widget is stretched or aligned in its parent. The Canvas Panel Slot gives us the option of setting pixel-based offsets from the edges of the parent.

In Blueprints, to access the Slot property of a widget use the GetSlotAs node.

Widgets

We’re not going to list every type of widget here and explain what each one does. The best thing you can do as a new UI developer is to try out each widget and see what you can do with it. What options it has for itself, if it can contain children what options are available on its children’s slots etc.

To give you an idea of where to start, here’s a list of widgets that are most commonly used. Try out each of these, read up about them, see what they do in the editor and how to use them from Blueprints.

  • Basic Widgets: Image, TextBlock, Button
  • Basic Containers: Overlay, Canvas, HorizontalBox, VerticalBox, UniformGrid
  • Intermediate Widgets: Border
  • Advanced Widgets: NamedSlot, RetainerBox

If you can understand how to use these widgets you should be able to make pretty much anything.

UserWidgets

Your experimentations above should have been done inside a UserWidget. So what’s a UserWidget? Similar to the way Blueprint Classes are used to create reusable objects with custom logic in maps, UserWidgets are used to create reusable objects with custom logic for user interfaces.

To use a concrete example, to set up the UI shown below, I could:

  • create a UserWidget called “PageTitle” and include it once
  • create a UserWidget called “LargeButton” and include a few copies of it on the page
Simple example showing a page title and large button user widget on an example widget. We can build up more complex widgets from re-usable individual pieces.
▲ Simple example showing a page title and large button user widget on an example widget. We can build up more complex widgets from re-usable individual pieces.

It’s important to note that I could create the same UI with just regular widgets, and copy-paste them to create identical-looking buttons. In the case of the PageTitle, there is only one instance of it on the page so I wouldn’t even have to copy-paste!

As a UI programmer and designer it’s up to you to decide whether or not to create UserWidgets for elements, whether they’d be better served by using existing Widgets, or whether it would be worth creating your own C++ based Widget. We will cover the latter of these in our more advanced tutorials on creating new UWidget subclasses in C++ and creating Slate classes in C++.

However in general there are some guidelines for when it’s better to create a UserWidget rather than copy-pasting a set of regular widgets:

  • When you want widgets with the same visuals in multiple parts of your UI.
  • When the widgets need complicated or very specific logic to set up their appearance or contents.
  • When you need to need to add and delete pre-styled widgets at run-time.

Document View

At first glance this panel seems fairly straightfoward: it shows you a preview of your UserWidget. You’ve probably been using it until now without thinking about it.

However there are a few features that are worth knowing about:

Desired Size

A widget’s desired size is something worth discussing further.

You can preview a widget’s desired size by choosing it from the drop-down menu at the top-right. This is the size at which it will display when its size is not overridden or stretched. This might sound kind of redundant but here are some examples:

  • if a widget is placed inside an Overlay, and its OverlaySlot settings are Horizontal-Left and Vertical-Top, it will display in the top-left at its desired size.
  • if a widget is placed in a Canvas, and its CanvasSlot setting “Size to Content” is set to true.

The desired size of a widget is especially useful when putting many widgets inside a with flow-layout style widget panels like HorizontalBox and VerticalBox.

You may have noticed when playing with UMG that placing more than one widget in a HorizontalBox or VerticalBox, that subsequent widgets are positioned based on the size of previous widgets. The desired size is what is used to calculate widget positions.

You can override a widget’s desired size in a few ways:

  • Size Box: Set a forced explicit height/width, or enforce a min/max size.
  • Canvas Panel: The widget’s desired size will be based on

Tricks of the Trade

The problem with Unreal and tutorials that I’ve seen so far is they often outline many different ways to solve a problem and don’t really give any suggestions on what kinds of approach work best.

In this series I will try to distill what I’ve learned in working on a large game in an industry-leading company, and what I’ve seen recommended by Epic.

I’m not trying to claim that this is the very best way to solve the problem, or all problems, but rather explain what did or didn’t work for me when working on a large game.

There are a few problems with creating entire UI systems in Blueprints:

  • You are likely to hit performance problems in large Blueprint-based UIs. Small one-use blueprints are fine, but for large, complicated logic that is called every frame, a Blueprint-based UI can really affect performance.
  • In Blueprints complicated logic is a nightmare to maintain. Anyone who’s written large Blueprint scripts can attest to the constant fight against spaghetti.
  • Separation of data acquisition and data presentation. With a Blueprint-only system, it’s easy to mix getting data from disparate sources, and formatting it for display. This makes future changes to data or display a real pain to implement as your display graph nodes are so closely tied to data-acquisition nodes.
  • Harder for many people to work on the UI at once. Blueprints are binary assets, meaning they’re impossible to merge, so only one person can edit them at a time. If the UI artist wants to update the appearance of a widget, and the developer is updating some Blueprint logic, they artist will have to wait. If most of the logic is in C++, both people are less likely to step on each others’ toes.

As we’ll see in the next chapter, we can solve a lot of these problems by moving some of our logic into C++.

Unreal UI Introduction Series

  1. An Introduction to Making UIs in Unreal
  2. Making UIs in Unreal with C++: Basics
    1. Creating C++-based UserWidgets
    2. Creating New UMG Widgets
    3. Creating New Slate Widgets (Coming soon)