Save / Load

In this section we will be covering one aspect that is common for the inventory and equipment system: the built-in save and load functionality. It is important to understand how both systems (specially containers) handles their data to be persistent even when the game is closed and how that data is organized in the blueprints that take care of this work.

When we talk about save and load functionality, 2 aspects must come to your mind:

  • Data persistent across different maps and linked to the current game session.
  • Data persistent across different game sessions.

To explain this concepts better, lets see which blueprints are related to each one.

GameInstance

This blueprint has the property of being persistent across maps. That means that if your player travels to a new map and the items container actors in the old map gets destroyed (and also the player), then all the data they holded in the old map would be lost, but this blueprint is the same instance always across different maps. Because of that, its the perfect place to keep all the data persistent even if the player goes to a different map.

In this project, the BP_DemoGI is used with the purpose explained before. Further we will see the functions, interfaces and variables it uses to act as we want.

However, all the save and load functionality isnt complete, because if the game is closed then the game instance is also destroyed, so the saved data there would be lost when we open the game later. We would need something like an external file on the hard drive to store the data we want to keep across different game sessions. For that reason, we need another kind of blueprint, which is the SaveGame class.

SaveGame

This blueprint is a special engine class created for the purpose we need. If we want to save or load something from an external file, Unreal can create a file with the .sav extension, which can hold all the data we need to keep across game sessions. The interface with that file is just an instance of the class SaveGame, so we just need to create a custom instance and add there all the variables we need to keep, just like a normal actor.

The instances created for this project are:

  • InventorySave: It holds all the variables needed to keep all the items containers and the player data related to the inventory functionality.
  • EquipmentSave: Holds all the variables needed for the equipment system.

Attention

Please give a look at the functions used to interact with this type of class here

Note

You may be wondering why the GameInstance blueprint used in this project is part of the demo content and doesnt come in the OpenRPG_Inventory folder if it is such an important blueprint for the save and load system to work properly. That is because you will probably have your own GameInstance already implemented with your own logic, so I prefer to give you the tools to easily integrate the logic into your own class rather than obey you to use the BP_DemoGI. Of course, if you are not using a custom GameInstance in your project at the moment, you can perfectly use this one.

Now that you understand the main blueprints used by this system and why they are used, lets see how they are connected between them and how the containers and the player sends and receive data from them.

Interfaces

To make the communication as transparent as possible, I have provided a few blueprint interfaces to abstract the communication between the components and your game instance. These interfaces are:

  • BPI_ContainerSave
  • BPI_PlayerInventorySave
  • BPI_EquipmentSave

Each of them have 2 functions with the syntax SaveXXXX and LoadXXXX depending on the type of data they save. For example, BPI_EquipmentSave has the functions SaveEquipment and LoadEquipment, while the interface BPI_ContainerSave has the functions SaveContainer and LoadContainer.

Those functions have a common input parameter called SaveName, which is a string label that identifies the data that you are going to save or the data you want to load. Think of the data as a package and this string as the ID of that package. This concept is very important because when you want to save or load any actor data, you need to pass this string to let the GameInstance identify what data package should be loaded or how it should be saved. You can just create an editable string variable in the actors that have any component data you want to handle (storage actors, the player, etc), and then use it when you save or load any data.

Note

In the BPI_ContainerSave interface you will notice there is an extra boolean input called DiskSaveable in the SaveContainer function. That is used to avoid saving containers that you would not want to be saved on disk. For example, you probably dont want to save the loot from a monster in the disk, but a storage or the player inventory should be.

There is also an extra interface called BPI_SaveManager. This one is used by the GameInstance to communicate with the SaveGame classes. Following the same schema as the other interfaces, it has the functions SaveDataToDisk and LoadDataToDisk. However, this functions doesnt have any input or output parameters, they are used to save or load a state of your game to the disk. Because of this, LoadDataToDisk can be called only when the game starts to dump all the saved data to your game instance, and then work with the data there. By the other hand, the SaveDataToDisk can be called when you reach some checkpoint in your map, or just when a UI menu button used to save the game is clicked.

If you are still reading this, congratulations! At the moment, you know which classes are used in the saving and loading process and why. Now you just need to add the interfaces commented above to your game instance and implement them with the data structure variables you prefer. My recommentation would be to use a map variable type, which is also called as dictionary. That is because the data stored has the structure of SaveName -> Data, which is the structure that this data type is made for, and the blueprint code needed is just a few nodes and works very fast. Just give a look at the BP_DemoGI blueprint and you will see how I have used it!