Save/Load
Save a State Machine when the player should be able to return to the same active state and variable values later. Use StateMachine.Save and StateMachine.Load for one State Machine, SaveManager for several State Machines in one file, or raw SaveData when State Designer needs to participate in a larger game save.
Save one State Machine
- Assign the State Machine that should be saved.
- Choose the file location.
- Call Save at the desired checkpoint and Load when that checkpoint should be restored.
The following component saves and loads one State Machine:
using Opsive.StateDesigner.Runtime;
using Opsive.StateDesigner.Runtime.Utility;
using UnityEngine;
public class Saver : MonoBehaviour
{
[Tooltip("The state machine that should be saved.")]
public StateMachine m_StateMachine;
[Tooltip("The location that the state machine should be saved.")]
public string m_SaveLocation = "Assets/StateMachine.save";
/// <summary>
/// Saves the state machine.
/// </summary>
public void Save()
{
m_StateMachine.Save(m_SaveLocation, SaveManager.VariableSaveScope.GameObjectVariables | SaveManager.VariableSaveScope.SceneVariables); // Saves the state machine and the GameObject/scene variables.
}
/// <summary>
/// Restores the state machine.
/// </summary>
public void Load()
{
m_StateMachine.Load(m_SaveLocation);
}
}
Graph variables are always included. The second Save parameter selects additional variable scopes; this example also saves GameObject and Scene variables.
Save multiple State Machines
Use SaveManager when several State Machines should be saved to and restored from the same file. Assign the State Machines to the array, then pass the same array and file location to SaveManager.Save and SaveManager.Load.
using Opsive.StateDesigner.Runtime;
using Opsive.StateDesigner.Runtime.Utility;
using UnityEngine;
public class Saver : MonoBehaviour
{
[Tooltip("The state machines that should be saved.")]
public StateMachine [] m_StateMachines;
[Tooltip("The location that the state machines should be saved.")]
public string m_SaveLocation = "Assets/StateMachine.save";
/// <summary>
/// Saves the state machine.
/// </summary>
public void Save()
{
SaveManager.Save(m_StateMachines, m_SaveLocation, SaveManager.VariableSaveScope.GameObjectVariables | SaveManager.VariableSaveScope.SceneVariables); // Saves the state machine and the GameObject/scene variables.
}
/// <summary>
/// Restores the state machine.
/// </summary>
public void Load()
{
SaveManager.Load(m_StateMachines, m_SaveLocation);
}
}
Include State Machine data in another save
StateMachine.Save can also return raw SaveData. This is useful when State Machine state belongs inside an existing game-save structure rather than its own file. The following example stores the State Machine data with the agent position and rotation.
See the SaveLoad sample scene for a working example.
/// <summary>
/// Stores all of the agent save data in one structure.
/// </summary>
private struct AgentSaveData
{
public SaveData StateMachineSaveData; // The state machine save data.
public Vector3 Position; // The Transform position.
public Quaternion Rotation; // The Transform rotation.
}
/// <summary>
/// Save the agent state.
/// </summary>
public void Save()
{
var saveData = m_StateMachine.Save();
if (!saveData.HasValue) {
return;
}
// Create the data structure which contains all of the values that should be saved.
var agentSaveData = new AgentSaveData() { StateMachineSaveData = saveData.Value, Position = m_Transform.position, Rotation = m_Transform.rotation };
if (File.Exists(m_SaveLocation)) {
File.Delete(m_SaveLocation);
}
try {
if (!Directory.Exists(Path.GetDirectoryName(m_SaveLocation))) {
Directory.CreateDirectory(Path.GetDirectoryName(m_SaveLocation));
}
var fileStream = File.Create(m_SaveLocation);
using (var streamWriter = new StreamWriter(fileStream)) {
streamWriter.Write(JsonUtility.ToJson(agentSaveData));
}
fileStream.Close();
} catch (System.Exception e) {
Debug.LogException(e);
return;
}
}
/// <summary>
/// Load the agent state.
/// </summary>
public void Load()
{
if (!File.Exists(m_SaveLocation)) {
return;
}
AgentSaveData agentSaveData;
var fileStream = File.Open(m_SaveLocation, FileMode.Open);
using (var streamReader = new StreamReader(fileStream)) {
var fileData = streamReader.ReadToEnd();
agentSaveData = JsonUtility.FromJson<AgentSaveData>(fileData);
}
fileStream.Close();
// Restore the values.
m_StateMachine.Load(agentSaveData.StateMachineSaveData);
m_Transform.SetPositionAndRotation(agentSaveData.Position, agentSaveData.Rotation);
}
How it runs
Saving records the current State Machine state and its graph variables. The variable-scope flags can include GameObject and Scene variables as well. Loading from the same file or SaveData restores those saved values and returns the State Machine to the saved state.
SaveManager applies the same process to an array of State Machines in one file. In the raw-data example, StateMachine.Save returns nullable SaveData, which is placed beside the agent transform data and serialized as one structure. Loading restores the State Machine data first, then the transform.
Verify a restore
- Run the State Machine until it reaches a recognizable state and set a known graph variable value.
- Save, then allow the State Machine and variable to change.
- Load the save and confirm that the saved state and graph variable value return.
- If GameObject or Scene scopes were included, confirm those selected variable values also return.
- For the raw-data example, also confirm that the agent position and rotation are restored.
Supported caveats
- Graph variables are always saved; other scopes must be selected with VariableSaveScope flags.
- The raw-data example stops if StateMachine.Save returns no value or if the requested file does not exist.
- Custom Actions and Conditions that hold additional runtime state must implement their supported save/load callbacks when that state needs to persist.