Search public documentation:
Document Summary: This document explains how to use the standard configuration system to create a save game system.
The configuration system of the Unreal Engine is quite powerful and can be used for more than just saving various settings. This document presents a method to create a save game system for an RPG like game. The save game system is pulled of by clever use of the UI datastore system and the
PerObjectConfigdirective. Before you start with this document, read the ConfigurationFiles document to understand how configuration files work in the Unreal Engine.
Sapitu (short for: Savegames Are Possible In The UDK) implements a simple save game system for an RPG like game. It has the ability to store a character and its inventory. You can download the full source code of this example here. Sapitu is split up into 4 classes. The download also contains 2 additional classes:
SapituPC, those are only used to play with the Sapitu system from within the game. The four classes of Sapitu are as follows:
Sapitu: This is the main class that manages various parts of the savegame system.
SapituCharacter: This class stored the character information.
SapituInventory: This class is an inventory item which can belong to a
SapituItem: This class is used as a database record to create
SapituInevntoryitems from. Instances of this class are read-only within the system.
loadItemDBinitialized the database of
SapituIteminstances. It requests all definitions of
SapituIteminstances in the configuration files. This is part of the UI datastore system in the Unreal Engine. This system is also used to retrieve information about game types, maps, and weapons in UT3. The
getItemmethod provides the means to look up an actual
SapituIteminstance in the current database given the object's name (the
Object.nameproperty). This name (or rather identifier) is an internal name. In your final game you would probably never show this to the player. The item database is used by the inventory system to look up the static definition of the inventory items. And it is also referenced to create new inventory items.
Sapituclass is also the entry point to load and create
SapituCharacterinstances. The character system makes use of the normal configuration system and the
PerObjectConfigclass directive. The
getCharactersmethod returns a list of IDs of all registered characters. Just like the identifier of the items in the database, this refers to the
Object.nameproperty. It is not a user friendly name, but an internal identifier. This identifier is required during the loading and creation of characters. Due to the way the name is used there is an important limitation. The name should not contain spaces and various other non-alphanumeric characters. It is best to stick to using just the alphanumerical characters for the id. Loading and creating of characters is actually similar. The both are done using the following code:
myChar = new(none, "MyCharId") class'SapituCharacter';The magic is within the second paramater for the new operator. This defines the name of the object to create. Because the
SapituCharactercharacter is defined as
PerObjectConfigit will load the configuration for an object with that given name. Because we want to make a clear distinction between loading and creating a new character in Sapitu we first check if a given identifier exists and then accept or reject the loading or creation. Loading of a character is not finished by creating the proper instance. We also need to initialize it's inventory. The
SapituCharacterstores a list of identifiers for
SapituCharacter.inventoryRecord). In a similar way these items are created and added to the list of inventory item instances
SapituCharacter.inventory. See the
loadCharactermethod for the exact implementation.
createInventorymethod create a new inventory item based on the given
SapituIteminstance. It constructs a unique identifier for the inventory instance, and then executes the special
new(none, inventoryId) class'SapituInventory'constructor to create inventory item. After that it will initialize various variables that were loosely defined by the
SapituIteminstance, like the level, weight, and value. The created inventory item does not belong to any character, in fact, it has not been saved to the save file. So when you exit the game, this instance is also lost. Saving of these items is left to the character instance when it has this inventory item in its inventory.
class SapituCharacter extends Object config(Sapitu) perobjectconfig;This means that certain variables in the class will be saved in the configuration file
UDKSapitu.ini, and that for each object a separate section is created. The latter is the result of the
perobjectconfigclass directive. The name of the object is of importance when saving the instance, because it defines part of the section name. This class defines various config variables for the character name (which is the human friendly name), health, etc. The character's inventory is stored in a different way. Because object variables cannot be a config variable we keep a record of the identifiers of all inventory items (which are strings). And save that list instead. This means additional administration is required to manager the inventory. Adding and removing inventory items must be done using the
removeInventorymethods. These method update both the
inventoryarray should not be modified directly. The
Sapituclass does do this during load, but that's a special case. Additionally, a saving of the character has to be done in an alternative way. You can not call
SaveConfigdirectly, because that does not save the inventory properly. The
savemethod iterates through the inventory array also calls
SaveConfigon each item, and then calls
SaveConfigon itself. This way everything belonging to this character is properly saved. Note: that neither the
SapituInventoryinstances are saved any where else in the Sapitu system.
SapituItemclass. This class is declared in the same way as the
SapituCharacterclass. Because this instance is based on an
SapituItemis needs special care with the regard to recreating to the proper reference. This is done by the
Sapituclass as mentioned earlier.
SapituInventoryclasses this class is a subclass of
UDKUIResourceDataProvider. It is not declared as
config, because the parent class is already defined as a config class. It is declared as
PerObjectconfig. Just like the other classes this also defines a couple of
configvariables. These are used to create an item definition. Also notice the
bSearchAllInis=trueentry in the defaultproperties section. This variable is part of the UI datastore system. It defines that the datastore system should go through all configuration files to find objects of this item. Loading of
SapituItemsshould not be done through the previously mentioned
new(none,id)constructor. To get all declared
SapituItems you should use the following code:
class'UDKUIDataStore_MenuItems'.static.GetAllResourceDataProviders(class'SapituItem', ProviderList);(see the
SapituItemsare declared by hand. At runtime the should never change. To declare an item simply create a configuration file. And add an entry like this:
[sword1 SapituItem] DisplayName=Short sword level=(min=1,max=10) weight=(base=10,levelMult=0.5) value=(base=5,levelMult=1)See the
UDKGame\Config\SapituItemDB.inifile in the source for more examples.
The characters and inventory items are saved in the file
UDKGame\Config\UDKSapitu.ini. See the file in the download for an existing "savegame". Note that all characters and inventory are saved in the same file. Also note that it is saved as plain text. You could worry about possible cheating. But for a offline single player game you can always cheat by modifying the save game. It does not matter if the save game is plain text or a binary file.
To play with this system, simply compile the source and start a game as follows:
SapituPCclass defined a bunch of console commands you can use to play with the system:
- createChar name
- loadChar charId
- createItem baseItem
- pickupItem itemId