Most solutions I found for making a loading screen in Unreal Engine rely on converting your whole project to use level streaming, setting up an UMG widget with a delay or similar ways which, depending on your project, can take a lot of time to set up and is not always reliable.
While making my own game Toontanks: Circuit Clash I discovered a different way to make a loading screen, which relies on the MoviePlayer. It is supported by all platforms and doesn’t require a lot to set up. It runs on a background thread and can be bound with pre-loading and post-loading map events. It also supports playing videos, audio, UMG and Slate.
Setting up MoviePlayer
This will require a custom C++ game instance class, which runs throughout the whole game and is one of the first objects to be initialised. I will use it as a base class for my game instance blueprint class, from which I will be passing my UMG widget.
To use the MoviePlayer we need to include the
MoviePlayer module in the build file. The
UMG module will also be needed to create the widget later on.
The Loading Screen with MoviePlayer
To create a UMG widget in C++, we need to have a hold of the class of the widget. So we define a
TSubclassOf<UUserWidget> property which will be provided via blueprints.
EndLoadingScreen functions like so in your game instance header file.
Bind the delegates in your
Init function and don’t forget to include the right header file.
FCoreUObjectDelegates holds a series of delegate bindings for the engine.
PostLoadMapWithWorld are called exactly when a map begins loading and finishes, respectively.
BeginLoadingScreen function, create the
UUserWidget and get its underlying slate widget pointer using
TakeWidget() as the MoviePlayer only accepts the native
Then, define your attributes with
FLoadingScreenAttributes. It’s a structure for all the attributes your loading screen will have. The explanation for each one can be found in the documentation. Assign your
SWidget pointer to the
And simply pass the structure into
And that’s it! Now you have an UMG widget that is displayed every time a new map is being loaded.
The MoviePlayer will automatically destroy the loading screen when the map has finished loading, but you can use
EndLoadingScreen for anything extra you would like to do.
And this is where we could stop, but I wanted to explore how to use this with Slate.
MoviePlayer runs on a background thread, the UMG widgets will not tick. In my game, I circumvented this by creating my own Slate widget for the part that needs to be animated, it’s also a good opportunity to learn about Slate in general. And it works if I just include it as part of the UMG widget I am using for the loading screen.
What I want from my animated widget, is essentially just two brushes. One sits on top of the other and rotates at some rate. Those brushes then can be exposed to the editor to provide custom images. The result should look something like this:
Setting up Slate
This is slightly more advanced, so I would advise you to follow the source code.
This will require private modules for Slate, which are already written in the build file, all you need to do is to uncomment it.
To make a Slate widget, two classes will be needed. A
UWidget and an
SCompoundWidget class. The
UWidget class is essentially a wrapper for the
SCompoundWidget class, and is the one that will be exposed to the editor.
SCompoundWidget is the base class from which all non-primitive widgets are built, and that’s what we need. It holds a protected member called
ChildSlot that we will use to provide the content and hierarchy of the widget.
Now, create a class, which I am naming
SLoading that inherits from the
SCompoundWidget class. By convention, Slate widget file names start with an
There are two major things to set up in this class. A
Construct method, where we will construct the widget, and the overridden
OnPaint function to define how it renders.
Define the rotating and the background brushes along with their setters and getters, as well as
Sequence and the
CurveHandle that will be used for the animation.
RotationSpeed is the rate at which the brush is going to rotate.
You can set up your arguments with
SLATE_BEGIN_ARGS and give them initial values like so.
cpp file, in the
Construct method, you will need to define your widget.
SNew defines a new component, in this case an overlay, which should have two slots defined with
+ SOverlay::Slot(). Each slot should have
VAlign properties to center it. And in each of those slots - the appropriate brush, which in this case are our images.
We also would like to assign the brushes with the initial values and set up a curve with a given start time and offset, and start playing it.
OnPaint function defines how the widget is rendered. Here we can set up its transform for rotation, as well as pass in colour and opacity parameters.
RotationTransform which takes our curve’s linearly interpolated value between 0 and 1, which we can use to determine the angle of the rotation and multiply it by our
RotationSpeed variable for the rate.
Then, create the geometry for the two brushes, using the calculated
RotationTransform for the rotating brush.
FVector2D(0.5, 0.5) centers the pivot from which it’s going to be rotating.
Now, make two boxes for each brush, passing in the geometry. The last argument is for the tint which allows it to be customised in the editor.
Now we can create the
Loading class which inherits from the
UWidget, in which we define the properties that will be exposed to the editor and synchronised with our
SynchronizeProperties() use the setters to sync the properties. The function is called after construction or when the editor needs to update modified state.
RebuildWidget() is called when the underlying
SWidget needs to be constructed, so this is where we pass in our initial parameter values to our SLoading widget on construction.
GetPalleteCategory() allows to set up our own category in the editor palette to organise it and find our custom made Slate widgets.
Now we can load up the editor and find our newly created Slate widget in the widgets palette.
And if we drag it in, we can customise its properties.
That’s it! Don’t forget to pass your widget in your game instance blueprint and it should be good to go.