Save System Module
Allows to save and load the progress of a RTS Engine game.
Features
- Save and load the state of game services, faction slots, entities and entity components.
- Customize the path, organization and format of the save files.
- Offers UI handling for saving the game and a game loader menu where saved game files are inspected and can be loaded.
- Comes with interfaces that can be implemented by your custom game services and entity components to load and save their state.
Requirements
- RTS Engine 2022.0.0 or higher
Installation
Get it from the Save System Module and import it into your project.
Guide
1. Setup Map Scene
In a map scene, create a new child object of the object where the Game Manager is attached. Preverbally have this new game object as a child of the Modules object to indicate that the services and components attached to it are not essential. Call it Save for example.
On this Save game object, add the following components:
1.1 Save Manager
The Save Manager is the main component that allows to save the state of a game in a map scene. It has the following fields:
- Save Options: What to allow when saving a game state?
- Replace Duplicate: In case the player attempts to save a game using a name that has been already used to save a previous game (a save file is already identified by that name) then enable to allow the new save game file to replace the old one with the same name or disable to disallow saving the game prompting the player to pick a different name for the saved game.
1.2 IO Handler
At the moment, the available IO handlers for saving games are:
1.2.1 JSON IO Handler
The Json Save IO Handler allows to save the game using the JSON format.
- Save Path: Folder path where the saved games files will be located. This path will be appended to Application.dataPath.
- Save Prefix: Prefix given to each saved game file. This prefix is then followed by the saved game's name.
- Save Format: The format of the saved game's files.
- Use Persistent Data Path: Use persistent data path as the path for saved game files? If disabled, the regular data path will be used which may cause issues on Mac or iOS. For more information, please check the Unity docs!
1.3 Save Game UI Handler
The Save Game UI Handler component provides a simple example on handling the UI aspects of the save system in a map scene. Assign the following fields in your map scene's UI:
- Save Name Input: UI Input Field intended for the player to input the saved game's name.
- Save Button: UI Button that the player clicks after inputting the saved game's name to finalize saving the game.
In your game, it is recommended to extend this component or build your own UI handling component.
The Save object that we created initially should now look like this after adding all the above service components:
Now you can save the progress of a game played in the map scene where the above components are added. Next we want to setup loading these saved games.
2. Game Loader Scene
Navigate to the Scenes folder in the module and open the SavedGameLoader scene.
This scene provides a menu that allows to fetch saved game files and load restore the game state each save file stores.
The main object in this scene is the SavedGameLoader UI canvas. This game object holds all the UI elements necessary to put this menu together as well as the components required to run the game loader and connect it with the RTS Engine.
Select the SavedGameLoader game object and let's explore the components attached to it.
2.1 Game Loader
The Game Loader is the main component responsible for loading saved games.
Game Code: Each saved game is saved with the game code inputted in the Game Manager component of the map scene it was saved in. Only saved game files with a game code matching this field will be loaded in this menu.
Prev Scene: The scene to load when leaving the lobby menu (when not starting a game).
Saved Game Slot Prefab: A UI object prefab that includes the Saved Game Slot component. This component manages each individual saved game slot. For each saved game file, an instance of this prefab is created. The base prefab can be found in the following path: RTS Engine -> Modules -> SaveSystem -> Prefabs:
- The Name UI field is a UI Text object that outputs the saved game's name while the Date UI is a UI Text object that shows the saved game's save date.
- The object that has the Saved Game Slot component attached to it must have a UI Button component as well. This allows it to be selectable.
- You can create your custom saved game slot prefab with your own logic behind it as long as the component handling it implements the ISavedGameSlot interface.
Faction Types: Input the Faction Types that faction slots in the saved games could have used.
NPC Types: Input the NPC Types that NPC factions in the saved games could have used.
Scene Loader: Defines properties regarding loading target map scenes. When the Load Async field is enabled, the target map scene would load asynchronously. The On Scene Load Start Unity event is called when the scene loading starts. For example, when the loading is done asynchronously, you can use the former event to show a loading screen while the target map scene is loading.
2.2 Game Loader UI Manager
The Game Loader UI Manager component is responsible for handling UI elements in the saved game loader scene.
Canvas: Main canvas that is the parent object of all lobby UI elements.
Saved Game Slot Parent: Parent object of all the saved game slot objects.
Saved Game Data Panel: Parent object of UI elements that display data about the currently selected saved game slot. When assigned and no saved game slot is selected, this panel is hidden.
Save Name UI: UI Text object used to display the current selected saved game slot's name.
Map Names: Possible choices for the map scenes that a saved game can have and the labels used for them in the UI elements.
Map Name UI: Text UI used to display the current selected saved game slot's map name.
Defeat Condition Names: Possible choices for the defeat condition that a saved game can have and the label used for them in the UI elements.
Defeat Condition UI: Text UI used to display the current selected saved game slot's defeat condition.
Saved Game Faction Slot Prefab: Prefab object used to display a selected saved game faction slots data. The base prefab which can be found in the path: RTS Engine -> Modules -> SaveSystem -> Prefabs, implements the ISavedGameFactionSlot interface and comes with assignable UI fields that allow to display the faction slot's color, name, type and NPC type.
Saved Game Faction Slots Parent: Parent object of each saved game faction slot data displayer (above prefab instances).
Load Game Button: UI Button that loads a selected saved game slot when clicked.
2.3 IO Handler
The same IO Handler used in the map scenes to saved the game must be be present in this game loader scene so that saved game files are loaded properly. Make sure the fields of the IO Handler are set to be the same as the ones assigned in the map scenes so that the same path is used to save and load the saved games files.
[We went through IO Handlers above](##1.2 IO Handler), so please refer to that section for more details on the types of IO handlers that you can use and the inspector fields on each one.
2.4 Other Game Loader Manager Components
- Game Loader Logger: Required to print log statements in the game loader menu.
- Game Loader Audio Manager: Handles playing audio clips and music in the game loader menu.
- Game Loader Player Message UI Handler: Handles displaying messages to the player in the game loader menu.
3. Saving Custom Components States
By default, the save system module handles the saving and loading of the essential RTS Engine components.
When you have created custom game services or entity components that you wish to save their state, please refer to the following instructions.
Please note that the activity status of your custom entity components, the target data of your custom target entity components, the basic properties of your custom attack components and the progress data of your progress entity components are also saved by default. It is only the additional properties that you wish to save and load that must be explicitly saved.
3.1 Save Formatter Service
First you need to add game service that implements the ISaveFormatter interface to your map scene:
using RTSEngine.Game;
namespace RTSEngine.Save.IO
{
public interface ISaveFormatter : IPreRunGameService
{
string ToSaveFormat<T>(T input);
T FromSaveFormat<T>(string input);
}
}
It includes two methods where one transforms a data structure or class into a string to be added to the save file and another that transforms the saved string into the original data type.
The following save formatter game services are included in the module. Make sure to use the one that conforms with the IO Handler service that you use to save the state of your games [as explained in details above](##1.2 IO Handler).
3.1.1 JSON Save Formatter
The Json Save Formatter is the formatter used with the Json Save IO Handler which allows to save the game the state of your custom components in the JSON format.
3.2 Saving Custom Entity Components
For your custom created entity components (ones that implement the IEntityComponent interface), grab the ISaveFormatter game service and use it to implement the ISavableEntityComponent interface's Save() and Load() methods. The below code provides an example for this implementation:
using UnityEngine;
using RTSEngine.EntityComponent;
using RTSEngine.Entities;
using RTSEngine.Game;
using RTSEngine.Save.Game.EntityComponent;
using RTSEngine.Save.IO;
public class MyCustomEntityComponent : MonoBehaviour, IEntityComponent, IEntityPostInitializable, ISavableEntityComponent
{
// IEntityComponent Properties & Methods
// ...
// ...
// Example data structure of savable data:
// Make sure the struct only has serializable fields and is serializable itself
[System.Serializable]
public struct MySavableDataType
{
public int savableInt;
public float savableFloat;
// ....
}
private MySavableDataType savableData;
// IEntityPostInitializable Methods
protected ISaveFormatter saveFormatter { private set; get; }
public void OnEntityPostInit(IGameManager gameMgr, IEntity entity)
{
// Other initialization logic
// ...
saveFormatter = gameMgr.GetService<ISaveFormatter>();
}
public void Disable() { }
// ISavableEntityComponent Methods:
public string Save()
{
// Convert the savable data into string form and return it to be saved by the IO Handler
return saveFormatter.ToSaveFormat<MySavableDataType>(savableData);
}
public void Load(string dataInStringForm)
{
// Get the savable data in string from and convert it back to the proper data type to be used.
savableData = saveFormatter.FromSaveFormat<MySavableDataType>(dataInStringForm);
// Use the loaded savable data to set the state of your custom component
// ...
}
}
3.3 Saving Custom Game Services
For your custom created game services (ones that implement either the IPreRunGameService interface or the IPostRunGameService interface.), grab the ISaveFormatter game service and use it to implement the ISavableGameService interface's Save() and Load() methods.
Make sure that the SaveCode property is unique among other game services that implement the ISavableGameService interface, because it is used by the save system to identify the services when loading hem.
The below code provides an example for this implementation:
using UnityEngine;
using RTSEngine.Game;
using RTSEngine.Save.Game.Service;
using RTSEngine.Save.IO;
public class MyCustomGameService : MonoBehaviour, IPostRunGameService, ISavableGameService
{
// ...
// Example data structure of savable data:
// Make sure the struct only has serializable fields and is serializable itself
[System.Serializable]
public struct MySavableDataType
{
public int savableInt;
public float savableFloat;
// ....
}
private MySavableDataType savableData;
// IPostRunGameService Methods:
protected ISaveFormatter saveFormatter { private set; get; }
public void Init(IGameManager gameMgr)
{
// Other initialization logic
// ...
saveFormatter = gameMgr.GetService<ISaveFormatter>();
}
// ISavableGameService Properties & Methods:
public string SaveCode => "unique_game_service_code";
public string Save()
{
// Convert the savable data into string form and return it to be saved by the IO Handler
return saveFormatter.ToSaveFormat<MySavableDataType>(savableData);
}
public void Load(string dataInStringForm)
{
// Get the savable data in string from and convert it back to the proper data type to be used.
savableData = saveFormatter.FromSaveFormat<MySavableDataType>(dataInStringForm);
// Use the loaded savable data to set the state of your custom component
// ...
}
}
Changelog
v2022.0.0
Release Date: 03/01/2022
Requirements
- RTS Engine 2022.0.0 or higher
Changes:
- Initial Release
v2022.1.0
Release Date: 03/12/2022
Requirements
- RTS Engine 2022.1.0 or higher
Changes:
- ADD saving and loading for tasks queues.
- FIX a bug that allows the Game Loader to load a game without recognizing the faction type or NPC type of one or more faction slots and setting it to null instead of printing an error.
v2022.2.0
Release Date: 04/27/2022
Requirements
- RTS Engine 2022.2.0 or higher
Changes:
- ADD support for saving and loading entity target commands that are launched as attack-move commands.
v2022.2.3
Release Date: 07/30/2022
Requirements
- RTS Engine 2022.2.3 or higher
Changes:
- ADD error logging when game services that implement the ISavableGameService interface do not all have distinct SaveCode properties.
v2022.3.0
Release Date: 08/25/2022
Requirements
- RTS Engine 2022.3.0 or higher
Changes:
- UPDATE saving and loading the time at which saved game slots are saved to use a global form to avoid unexpected issues that may arise when loading a save game slot file from a different machine.
v2022.3.1
Release Date: 12/05/2022
Requirements
- RTS Engine 2022.3.1 or higher
Changes:
- FIX saving and loading the positions of rallypoint components.
- UPDATE saving and loading faction multiple entity attack components and their respective active attack objects if there is any.
v2023.0.0
Release Date: 05/10/2023
Requirements
- RTS Engine 2023.0.0 or higher
Changes:
- ADD the option to choose between data path and presistant data path as the path prefix for the save files in the JSON based IO handler.
- UPDATE all UI related components to use TextMeshPro instead of Unity Text.