Introduction
MMTools comes with two complementary systems, in MMTools/Core/MMSaveLoad:
- the MMSaveLoadManager, a static class that saves any serializable object to a file, and loads it back
- the MMPersistenceManager, a component that uses it to save and restore the state of objects in your scenes (their position, active state, or anything you decide)
MMSaveLoadManager
The MMSaveLoadManager can save any object marked as [System.Serializable] to a file, and load it back. It’s what the MMSoundManager uses to save its settings, for example.
using MoreMountains.Tools;
using UnityEngine;
[System.Serializable]
public class GameProgress
{
public int Level;
public int Coins;
public float PlayTime;
}
public class ProgressManager : MonoBehaviour
{
protected const string _saveFolderName = "MyGame";
protected const string _saveFileName = "progress.data";
public GameProgress Progress = new GameProgress();
public virtual void SaveProgress()
{
MMSaveLoadManager.Save(Progress, _saveFileName, _saveFolderName);
}
public virtual void LoadProgress()
{
GameProgress loadedProgress = (GameProgress)MMSaveLoadManager.Load(typeof(GameProgress), _saveFileName, _saveFolderName);
if (loadedProgress != null)
{
Progress = loadedProgress;
}
}
}
Load returns null if the file doesn’t exist (you can also check that beforehand with MMSaveLoadManager.SaveFileExists(fileName, folderName)). You can also delete saves with DeleteSave(fileName, folderName), DeleteSaveFolder(folderName) or DeleteAllSaveFiles().
Where are files saved?
Files are saved in Application.persistentDataPath/MMData/YourFolderName/. When running in the editor though, they’re saved in Assets/MMData/YourFolderName/, so you can easily inspect (or delete) them. You can delete all of them at once via the Tools > More Mountains > Delete all saved data menu.
Save methods
By default, files are saved in binary. You can pick another method, either by setting MMSaveLoadManager.SaveLoadMethod from code, or by adding a MMSaveLoadManagerMethod component to your scene, and picking a method in its inspector. The available methods are:
- Binary (the default)
- BinaryEncrypted
- Json: human readable, handy for debugging
- JsonEncrypted
The encrypted methods use the EncryptionKey you define on the MMSaveLoadManagerMethod component. Note that the method (and key) used to load a file has to be the same as the one used to save it. Also keep in mind that this encryption will deter casual tampering, but the key lives in your build, so don’t rely on it for anything sensitive.
MMSaveLoadManager.SaveLoadMethod = new MMSaveLoadManagerMethodJson();
MMPersistenceManager
The MMPersistenceManager saves and restores the state of objects in your scenes. Typical uses: remember which chests have been opened, which enemies are dead, or where that crate was pushed, when the player leaves a level and comes back to it, or between sessions.
Setup
- add a MMPersistenceManager component to an object in your scene (it’s a persistent singleton, so it’ll survive scene loads)
- add a MMPersistent component to each object whose state you want to save. From its inspector, pick what to save: position, local rotation, local scale, active state, and the enabled state of its components. Each persistent object gets a unique ID (GUID), generated automatically, and that you can regenerate via its inspector button. You can add an optional suffix to make it more readable.
- trigger saves and loads
Saving and loading
Data is handled in two steps: objects get saved to memory (one entry per scene), and memory gets saved to a file. On load, it’s the opposite. The manager’s inspector has buttons for each of these steps, which is handy for testing. From code, you can call them directly on the manager:
MMPersistenceManager.Instance.SaveToMemory(); // stores the current scene's objects' state in memory
MMPersistenceManager.Instance.SaveFromMemoryToFile(); // writes all scenes' data to file
MMPersistenceManager.Instance.LoadFromFileToMemory(); // reads the file into memory
MMPersistenceManager.Instance.LoadFromMemory(); // applies the current scene's data to its objects
But the simplest way is to use MMGameEvents. The manager listens for MMGameEvents named Save (save to memory, then to file), Load (load from file, then apply), and also SaveToMemory, LoadFromMemory, SaveToFile and LoadFromFile for each individual step. Each of these can be turned off from the manager’s inspector.
MMGameEvent.Trigger("Save");
A common pattern is to trigger a SaveToMemory before leaving a scene, and a LoadFromMemory once a scene has loaded, and to save to file at checkpoints or when quitting. Since these are regular MMGameEvents, you can also trigger them from a MMF Player, with the MMGameEvent feedback.
The manager broadcasts a MMPersistenceEvent after each step (DataSavedToMemory, DataLoadedFromMemory, DataSavedFromMemoryToFile, DataLoadedFromFileToMemory), if you need to react to it.
Saving your own data
MMPersistent only saves transform and activation data, but you can save anything you want by creating your own persistent class. The easiest way is to extend MMPersistentBase, which takes care of the GUID, and override its OnSave and OnLoad methods. OnSave returns a string (usually json), and OnLoad receives that same string:
using MoreMountains.Tools;
using UnityEngine;
public class PersistentChest : MMPersistentBase
{
public bool Opened = false;
[System.Serializable]
public struct Data
{
public bool Opened;
}
public override string OnSave()
{
return JsonUtility.ToJson(new Data { Opened = this.Opened });
}
public override void OnLoad(string data)
{
Opened = JsonUtility.FromJson<Data>(data).Opened;
// update your chest's visuals here
}
}
If you can’t extend MMPersistentBase, you can also implement the IMMPersistent interface (GetGuid, OnSave, OnLoad and ShouldBeSaved) on any class.