Custom Save Objects
How to use Custom Save Objects.
This allows you to save your own UObject as a Savefield, useful for complex collections of data.
What are CustomSaveObjects?
Sometimes, instead of using multiple Savefields, it’s convenient to pack lots of data into one Savefield. Especially if you would like arrays of that data.
Custom Save Objects allow you to do this. They are a Savefield type that stores a UObject that you define.
An Example
Imagine you are making a fantasy RPG, and your player gathers many party members over the course of the game.
Each party member has a lot of data associated with them (name, experience points, hitpoints, mana, weapons, stats, etc). The player might have 10 different party members saved in their game. Spreading all this data out over individual Savefields would be difficult to manage!
This is an excellent use-case for a Custom Save Object. If you create a UObject that stores all relevant data for a party member, you can then create 1 Savefield, which stores an array of 10 Custom Save Objects. Now if you know a party member’s index, you can easily access and modify all of their saved data.
What Can You Save in Custom Save Objects?
The UObject you create as your Custom Save Object can contain any properties you like.
However, only serialize-able properties will save and load successfully.
Serialization is the process of translating an object into a format that can be stored (read and written).
Be conscience of what you attempt to save. Unreal implements serialization in many of its included types and data structures, but not everything supports it or is intended to be serialized.
Guidelines
I recommend adhering to these guidelines, especially if you’re a beginner to serialization in Unreal:
- Stick to basic types for the properties in your custom save objects (FNames, FStrings, bools, ints, floats, etc).
- You can also have structs and arrays of properties in your custom save objects.
UObjects
If you know what you’re doing, you can store other UObjects as properties in a Custom Save Object.
This is generally not necessary, but it is an option if you proceed carefully.
Creating CustomSaveObject Classes
To create a Class to use as a CustomSaveObject, create a subclass of UProtoProfilesCustomSaveObject. You can do this in Blueprint or C++.
After you create your class, any variables you add to the class will be saved.
The UObject is serialized recursively, so any additional UObject variables stored in it will also be serialized.
Notes on Pointers
Your CustomSaveObject value is a pointer to a UObject. But note that the subsystem will get and set copies of the UObject you pass in and out.
This means that when you Get the value of your CustomSaveObject Savefield, and you make changes to that UObject, they will not be applied to the saved data until you Set the Savefield with your UObject pointer.
In other words, despite being a pointer, you treat it the same as any other Savefield.
Potential Performance Concerns
Depending on how large your CustomSaveObject class is, the subsystem’s behavior of making duplicates could have undesired overhead (more than a typical Savefield, at least).
A way you can avoid extra overhead is to only Get and Set CustomSaveObject Savefields when necessary. For example:
- You can Get the value once when a level starts.
- Manipulate your game logic’s copy of the pointer, referencing it directly outside of the Proto Profiles Subsystem.
- Finally, Set the value to the subsystem only when a save needs to be performed.
Avoid Infinite Loops in Serialization
Because CustomSaveObjects are serialized recursively, it’s possible to create a situation where an infinite loop occurs during serialization.
If you have a CustomSaveObject that stores a pointer to another UObject, and that UObject stores a pointer to the CustomSaveObject, an infinite loop will occur!