Now that I have some big monsters running around, I need to keep them around. Normally all the information about a monster (size, position, plants on it, etc.) vanishes when the player leaves the level or quits the game. But I want to reproduce that monster later on! So I need to store the monster data somewhere that persists between level changes AND can be saved to disk for later use. This post goes over what data I needed to save and also discusses the hacks to get around some odd Unreal Engine 4 behavior regarding saving and loading.
Level Changes in UE4
As a first step I need to keep track of monster data between level changes. There are many level changes when playing a typical game. Some examples of different levels:
- The Main Menu
- Avatar Customization
- World Map
- Actually Running Around With Your Avatar And Doing Stuff (which I’ll call the “Tile Explorer”)
Each one of these “screens” is typically implemented internally as a different level and when you switch from one to another you throw away all the stuff from the previous level. Speaking in terms of UE4 code and classes, the level is a UWorld containing all the stuff of the level. Switching levels replaces the current UWorld with a new world. Most the objects in the previous world get destroyed with a few exceptions.
Which objects don’t get destroyed when switching levels? The two objects I’ll talk about here are the PlayerState and the GameInstance.
The PlayerState object holds info about a specific player that persist through player death. This would include things like score, team, and character class. If you add your own custom properties to the PlayerState, you can then override the CopyProperties method which will copy your properties from the old PlayerState to the new PlayerState.
The GameInstance is an object associated with currently running game. As such, it exists outside of the normal level loading sequence. Stuff in the GameInstance will avoid destruction until the program ends. Note that you shouldn’t just throw everything in there, of course. If some data is only relevant to the current level, you should just store it in the PlayerState or in the level itself.
Which Parts Need to Remain When Switching Levels?
Based on this information, some of the monster information needs to persist through level changes and some of it can be thrown out. To decide which parts to keep, I imagine what data needs to be maintained and available all the time. For example, if the player is running around in the Tile Explorer and quits back to the World Map (a level switch) which information should be available from the world map for each monster?
- Where the monster is on the world map (the name of a hex tile on the world map)
- What kind of monster it it (blob, giant worm, giant turtle)
- What plants have been growing on it.
- Enough information to recreate the monster when the player goes back to the Tile Explorer. This includes things such as: the type of monster, which meshes to use when creating the monster, and what plants are on the monster.
The informationn that needs to persist between levels is put into a separate object called the Monster Durable State. Meanwhile the transient object Monster Instance holds transient stuff that goes away once the player leaves the Tile Explorer such as render shaders and collision geometry. The Monster Instance holds a pointer to the Durable State.
The Durable State is dumped inside the GameInstance so that it doesn’t vanish on a level change. When the player leaves the Tile Explorer and returns to the World Map, this triggers a level change. The Monster Instances in the old level get destroyed but the Durable States do not.
This is already pretty long so I will go over the Saving and Loading of monster state (along with the rest of the world data) next time.