Between the Folds #002 - Level Streaming


We wanted to make the whale that Ernest Hemingwhale proceeds through in WHALIEN feel like a single coherent being. That’s why we decided to rely on LevelStreaming to be able to refrain from using loading screens.

Blocking Loads

Unreal Engine 4 already provides a comfortable way of streaming levels. However, we decided against using their automatic and distance-based level streaming system because our levels are heavily intertwined and we want to have absolute control on which levels should be loaded at any given time. For manual level streaming, UE4 provides the LoadStreamLevel-Node in Blueprints.

The LoadStreamLevels-Node

(The LoadStreamLevel Node can load a level by specifying its name)

As the name may imply, the ShouldBlockOnLoad-flag causes some lag when loading in the level, so we tried to keep it unchecked. However, we quickly noticed that the PhysicsConstraints in our streamed levels require this flag to be set in order to function properly.

Loading PhysicsConstrained chains

(Influence of ShouldBlockOnLoad on chains constrained by PhysicsConstraints. Left: without ShouldBlockOnLoad; right: with ShouldBlockOnLoad)

Our goal is to reduce the amount of BlockOnLoad calls to LoadStreamingLevel, in order to make the streaming not be noticeable to the player at all. For this, we are planning to split up each level into two: one level containing all PhysicsConstraints and one containing all other actors. This way we only need to make the blocking call for the few critical actors, which should reduce the loading spikes drastically - especially on slower machines.

Hiding the levels

Besides this optimization, we found another way of decreasing the amount of lag when the [i]ShouldBlockOnLoad [/i]flag is set: loading all streaming levels at the start of the game and then hiding them. This requires the levels to only be unhidden instead of re-loaded, but it comes at the cost of additional memory requirements.

Hiding a level without actually unloading it is one of the features that is accessible through C++ but not through Blueprints. In C++, the ULevelStreaming-Class possesses the functions SetShouldBeLoaded() and SetShouldBeVisible() to help us define what levels should be loaded and which of those should actually be rendered and processed.

While with the Blueprint function only one level can be loaded/unloaded at a time,  the SetShouldBeLoaded() and SetShouldBeVisible() functions in C++ are handled in parallel in the background, allowing the loading of multiple levels at once. Moreover, each UStreamingLevel possesses delegates that are called whenever it is loaded/unloaded/hidden/unhidden, so we can react to the level load action being completed. 

ULevelStreaming delegates

Code sample of subscribing to the OnLevelShown-delegate and executing the callback function once it is triggered

The C++ implementation allowed us to create a function that takes in multiple levels to load and executes a callback function once all load actions are completed. 

Transferring actors

Another issue we faced was that when a level is unloaded, all actors in that level are also unloaded. Since we don’t want the player to lose the actors they are carrying with them, we need to transfer them to the newly loaded level.

This transfer is accomplished by removing them from the old level and adding them to the array of actors of the new level. This way, the player can take all his favorite objects with him through the entire game!

Transferring actors

Code sample for transferring an actor from one level to another

On transferring between levels, every actor’s velocity is reset, so we store their linear and angular velocity before the transfer and re-apply it afterwards.

Transferring actors in action

The previous level being unloaded. Left: the carried actor belongs to the previous level and is removed; Right: the carried actor was transferred to the new level and remains in play

Conclusion

Our goal for WHALIEN is to create a seamless experience without any loading screens, which we achieve utilizing a system of LevelStreaming-Triggers. These triggers are placed in each level and load the following level when the player first triggers them.

As always, if you have any other approaches or tricks for level streaming, we would love to hear about them.

- Andi, Ralf

Leave a comment

Log in with itch.io to leave a comment.