Save & Load
The StoryFlow Unity package provides a complete save/load system for persisting global variables, character state, and once-only option tracking across play sessions. All operations go through StoryFlowManager.Instance.
Overview
StoryFlow saves are story checkpoints, not mid-sentence bookmarks. They capture the cumulative result of all player choices - variable values, used options, character modifications - but not the current position in a dialogue tree. Your game is responsible for knowing which script to start when loading a save, typically by storing a "current scene" identifier in a global variable.
Checkpoint Model
Save between dialogues, not during them. The system is designed for "between conversation" checkpoints. A common pattern is to create a global string variable called currentScene and update it each time the player enters a new scene. On load, read this variable and use it to start the correct script.
Saving
Call SaveToSlot on the manager singleton to serialize the current global state to a named save slot:
bool success = StoryFlowManager.Instance.SaveToSlot("slot1");
if (success)
{
Debug.Log("Save successful.");
} Parameters:
slotName(string) - The name of the save slot. Can be any non-empty string.
Returns: bool - true if the save was written successfully, false if an error occurred.
Saving during active dialogue
SaveToSlot will succeed while dialogue is active. Local script variables (which are per-script and transient) are not included in the save. Only global state is persisted. If you need a clean save, wait until the dialogue session ends.
Loading
Call LoadFromSlot to read a save file and replace the current global state with the saved values:
bool success = StoryFlowManager.Instance.LoadFromSlot("slot1");
if (success)
{
Debug.Log("Load successful. Start the appropriate script now.");
// Read a global variable to know which script to resume
// component.StartDialogue("scripts/chapter2");
}
else
{
Debug.Log("Load failed. Slot may not exist or dialogue is active.");
} Returns: bool - true if the load succeeded, false if the slot does not exist, deserialization failed, or a dialogue is currently active.
Loading refuses while dialogue is active
LoadFromSlot checks whether any StoryFlowComponent currently has an active dialogue. If so, the load is rejected and the function returns false. This prevents a class of bugs where:
- Variable values change underneath a running script, causing incorrect branching
- Character data is swapped mid-dialogue, producing mismatched names or portraits
- Once-only option state is cleared while the player is viewing those options
Always ensure all dialogues are stopped before attempting to load:
var manager = StoryFlowManager.Instance;
if (manager.IsDialogueActive())
{
Debug.LogWarning("Cannot load while dialogue is active.");
return;
}
bool success = manager.LoadFromSlot("slot1"); Managing Save Slots
Two utility methods let you check for existing saves and delete them:
var manager = StoryFlowManager.Instance;
// Check if a save exists
bool exists = manager.DoesSaveExist("slot1");
// Delete a save
manager.DeleteSave("slot1"); These are useful for building save slot UIs - gray out empty slots, show confirmation dialogs before deleting, and enable or disable a "Continue" button based on whether a save exists.
What Gets Saved
When you call SaveToSlot, the following data is serialized into a JSON file:
- Global variable values - All project-level variables with their current values, including arrays of any type. On load, variables are matched by ID and only the
Valuefield is updated, preserving the variable's metadata from the project asset. - Character variable values - Runtime character data with any modified character variables. Characters are matched by path, and their variables are matched by ID within each character.
- Once-only option tracking - The set of
nodeId-optionIdkeys for dialogue options that have been used and should not appear again.
The following data is not saved:
- Local script variables - These are per-script and reset every time a script is entered. They are transient by design.
- Current dialogue position / execution state - The current node, call stack, and flow stack are not persisted.
- Audio playback state - Any currently playing dialogue audio is not captured.
Async Operations
The StoryFlowSaveHelpers static class provides async variants of save and load for non-blocking file I/O. These are useful if you need to avoid frame hitches on platforms with slow storage:
using StoryFlow.Utilities;
var manager = StoryFlowManager.Instance;
// Async save - takes the individual state dictionaries, not the manager itself
bool success = await StoryFlowSaveHelpers.SaveAsync(
"slot1",
manager.GlobalVariables,
manager.RuntimeCharacters,
manager.UsedOnceOnlyOptions
);
// Async load - returns raw save data
StoryFlowSaveData data = await StoryFlowSaveHelpers.LoadAsync("slot1");
if (data != null)
{
// For most use cases, prefer the synchronous LoadFromSlot() which
// handles deserialization AND applies the data to the manager state.
// The async variant is lower-level and returns raw data only.
} When to use async
For most desktop and console games, the synchronous SaveToSlot and LoadFromSlot methods are fast enough. Consider the async variants for mobile or WebGL platforms where file I/O can be slower, or if your save data is particularly large (many variables, many characters).
Save Location
Save files are written to:
Application.persistentDataPath/StoryFlow/Saves/{slotName}.json
The directory is created automatically if it does not exist. The save format is JSON with a Version field for forward compatibility. If the save format changes in a future plugin update, the deserializer can detect older saves and migrate them.
// Example save file path on Windows:
// C:/Users/{user}/AppData/LocalLow/{company}/{product}/StoryFlow/Saves/slot1.json
// Example save file path on macOS:
// ~/Library/Application Support/{company}/{product}/StoryFlow/Saves/slot1.json Resetting State
The manager provides three reset methods for returning state to project defaults:
-
ResetGlobalVariables()- Resets all global variables to their default values as defined in the project. Does not affect characters or once-only options. -
ResetRuntimeCharacters()- Resets all character definitions to their project defaults, reverting any in-game modifications to character variables, names, and images. -
ResetAllState()- Resets global variables, runtime characters, and clears the once-only options tracking. This is a full reset intended for a "New Game" flow.
var manager = StoryFlowManager.Instance;
// New Game flow
manager.ResetAllState();
// Optionally delete the old save
manager.DeleteSave("slot1");
// Start the opening script
component.StartDialogue("scripts/intro"); Tracking the current scene
Since StoryFlow saves do not include execution position, you need a way to know which script to start after loading. A common pattern is to create a global string variable called something like currentScene and update it each time the player enters a new scene. On load, read this variable and use it to start the correct script.