Save and Load
The Inventory Engine comes with save and load mechanisms, so you can have persistent inventories across scenes, but also across sessions. When saving, the InventoryEngine will generate (or overwrite) a file on disk for each of your inventories in your scene. It uses MoreMountains’ SaveLoadManager, which will handle file location depending on your device for you, so it works on PC, but also on mobile without effort.
Requesting save and load
For inventories to actually save and load data, they’ll need to be marked as Persistent:true in their Inventory component’s inspector and will need to receive a save or load request. If you’re using the Corgi Engine or TopDown Engine, these save and load requests are already built-in so you don’t have to worry about them.
If you look at the PixelRogue demos provided with the engine, you’ll see examples of these requests.
Load (which will read the saved file if it exists and fill the inventory with the saved content) is triggered by the InventoryDemoGameManager class, on Start(). In your game, you can request a load from anywhere in your scripts, using the following line :
MMEventManager.TriggerEvent(new MMGameEvent("Load"));
As you can see, the InventoryEngine uses MoreMountains’ Event Manager class to propagate and catch events throughout the various classes. What this line does is trigger a Game Event called “Load”. All classes can decide to listen for this event, and when it’s received, do an action. In this case, the Inventory class listens for Load events, and when received, loads the saved file and fills itself with its contents.
Saving is done just as easily, using the following line :
MMEventManager.TriggerEvent(new MMGameEvent("Save"));
Again, in the Corgi Engine or TopDown Engine this is built-in. In your game you’ll need to explicitly call it when you deem it necessary. In the PixelRogue demo that comes with the engine, this is called when exiting a room, but you could also decide to have a menu item to actually save, or maybe call that save event when exiting the game.
Deleting inventory saves
You can delete individual saves by calling the ResetSavedInventory() method on a reference of your target inventory :
someInventoryReference.ResetSavedInventory();
And you can delete all inventories at once like so (of course, feel free to change the folder name to match your context) :
string _saveFolderName = "InventoryEngine";
MMSaveLoadManager.DeleteSaveFolder (_saveFolderName);
When in Editor, you can delete saves via the Tools > More Mountains > Reset all saved inventories. This will only delete editor saves, and won’t have an impact on any saves you may have on your build(s).
Handling multiple save files
Maybe in your game you’d like to be able to have multiple save tracks. In this case, you’ll want to make sure you override the save path before saving and loading. Simply specifiying a folder name will do, and these can be anything you want. So for example, if we had two save tracks (A and B), we could do the following :
// set the path to save slot A
Inventory._saveFolderName = "InventoryEngine/SaveA/";
// trigger a save of current inventories on slot A
MMEventManager.TriggerEvent(new MMGameEvent("Save"));
// then later on, in a different session, if Player B is playing, we can load slot B like so :
// set the path to save slot B
Inventory._saveFolderName = "InventoryEngine/SaveB/";
// trigger a load of current inventories on slot B
MMEventManager.TriggerEvent(new MMGameEvent("Load"));
Persisting other objects
The Inventory Engine also lets you persist more than just inventories. In fact, it lets you persist any object you want, meaning that when exiting a scene you can save the state of an object, and the next time you come back to it, whether it’s in the same session, or days later in a different session, that object’s state will be loaded and restored. You can see an example of that in the PixelRogueRoom1 and PixelRogueRoom2 scenes. If you pick item pickers in room 1, then move to room 2 via the door at the top, then come back to room 1, these pickers will still be gone. Likewise, room 2 contains a ball your character can push. Its transform state is saved when you exit the room, and restored when you enter it again, so it feels like it’s remained at the same spot.
To do this, you’ll need a MMPersistenceManager in your scene. This will handle the saving and loading of all objects with a component that implements the IMMPersistent interface. From its inspector you’ll be able to define a unique PersistenceID and a number of events for it to listen to. Most of the time you’ll want to just leave this to their default states.
Now for objects to keep their state, you’ll need to add components that implement the IMMPersistent interface to them. By default, the engine comes with the MMPersistent component. That one will let you save the object’s position, rotation, scale and active state, which most objects rely on. Simply add that component to objects you’d like to save, and you’re done.
Saving other things than position and active state
It’s very likely that in your game you’ll want to save more than just these values. For that, you’ll want to create new classes that implement the IMMPersistent interface and save your own properties.
A good way to do it is to extend the MMPersistentBase class, which comes with GUID management so you just have to focus on save and load.
Let’s say we want to persist the state of a Light’s intensity and color across sessions. In the following example, we’ll create a component to put on our Light that will handle that for us :
using System;
using UnityEngine;
using MoreMountains.Tools;
public class PersistentLight : MMPersistentBase
{
// we declare a Data structure with variables in which to store the light properties we want to persist
[Serializable]
public struct Data
{
public float LightIntensity;
public Color LightColor;
}
// on Awake we store our Light component for future access in the save/load methods
private Light _light;
void Awake() { _light = GetComponent<Light>(); }
// on save we store our current intensity and color into a new Data object, and we return it
public override string OnSave()
{
Data saveData = new Data { LightIntensity = _light.intensity, LightColor = _light.color };
return JsonUtility.ToJson(saveData);
}
// on load, we apply the loaded values to our light
public override void OnLoad(string data)
{
_light.intensity = JsonUtility.FromJson<Data>(data).LightIntensity;
_light.color = JsonUtility.FromJson<Data>(data).LightColor;
}
}
Now we can just add that component to our Lights in our scene, and they’ll automatically keep their values across sessions.
Fine control of the PersistenceManager
The MMPersistenceManager saves data to memory and can save to file. By default it does both on Save and Load events, just like the Inventory class does. But from its inspector you can separate these, for example you may decide that in your game data is only saved to Memory but the Player has to actively press a button or reach a certain point to save to file. For this, the Persistence Manager uses the following events :
// saves data from your objects in the scene to memory
MMEventManager.TriggerEvent(new MMGameEvent("SaveToMemory"));
// loads data from memory and applies it to objects in the scene
MMEventManager.TriggerEvent(new MMGameEvent("LoadFromMemory"));
// saves data from memory and to file
MMEventManager.TriggerEvent(new MMGameEvent("SaveToFile"));
// loads data from file and to memory
MMEventManager.TriggerEvent(new MMGameEvent("LoadFromFile"));
On the Persistence Manager, you’ll find SaveToFileOnSaveEvents and LoadFromFileOnLoadEvents settings. They’re true by default, and mean that a Save event will cause a save from objects to memory, and from memory to file, while a Load event will load from file to memory, and from memory to objects.
Where is data stored?
Data is stored in the PersistentDataPath (see Unity’s docs for exact paths).