How to Implement Save and Load Functionality in the Undertale‑Changer Template

The Undertale‑Changer Template provides a complete JSON‑based save system centered around SaveController, SaveService, and SaveBoxController, allowing you to serialize PlayerControl data and FactTables to Assets/Data/ and restore them later with automatic file management.

The Undertale‑Changer Template is a Unity-based framework that ships with a production-ready save system. You do not need to build serialization logic from scratch; instead, you interface with three core components that handle JSON conversion, file I/O, and UI flow according to the arch-aik/undertale-changer-template source code.

Understanding the Core Save Architecture

The save system relies on three coordinated components:

How the Save System Works

The Saving Process

When the player initiates a save, the following chain executes:

  1. UI ConfirmationSaveBoxController.HandleConfirmationInput detects the Z key press and calls ProcessSaveConfirmation.
  2. Service TriggerProcessSaveConfirmation invokes SaveService.SaveGame(), which delegates to SaveController.SaveData(PlayerControl player, string dataName).
  3. JSON SerializationSaveData ensures the Assets/Data folder exists, serializes the PlayerControl instance using JsonConvert.SerializeObject, and writes it to {dataName}.json.
  4. FactTable Persistence – The same method calls SaveFactTablesToJson to capture the current state of all FactTable assets in Resources/Tables.
  5. Preferences UpdateSaveService.SaveGame finishes by calling SavePlayerPreferences to cache settings like language and resolution.

The Loading Process

Loading reverses the flow via SaveController.LoadData(string dataName):

  1. File ManagementLoadData first calls SortAndRenameData to ensure save files follow a continuous sequence (Data0.json, Data1.json, etc.).
  2. Instance Creation – A fresh PlayerControl ScriptableObject is created with ScriptableObject.CreateInstance<PlayerControl>().
  3. DeserializationJsonConvert.PopulateObject reads the JSON file and populates the instance fields (name, level, position).
  4. State Restoration – The loaded PlayerControl is cached in UsersData, and LoadFactTablesFromJson re‑hydrates the event system state.

FactTable Persistence

FactTables are ScriptableObjects used by the event system (EventTable, RuleTable). The save system walks the Resources/Tables folder, serializes each table’s facts list to JSON, and restores it on load, ensuring events continue exactly where they left off.

Implementing Save Functionality

Triggering a Save from the UI

Use the built-in save dialog to handle confirmation flow automatically:

using UnityEngine;
using UCT.Overworld;

public class MySaveButton : MonoBehaviour
{
    public void OnClick()
    {
        // Opens the built-in save UI and handles confirmation
        SaveBoxController.Instance.OpenSaveBox();
    }
}

Calling SaveBoxController.Instance.OpenSaveBox() presents the player with the standard save dialog, which eventually reaches SaveService.SaveGame() and writes the file.

Saving Custom Data Automatically

Because SaveController.SaveData serializes the entire PlayerControl instance, you can extend the save payload by adding fields to the PlayerControl ScriptableObject:

[Serializable]
public class PlayerControl : ScriptableObject
{
    public List<string> inventory = new List<string>();   // Custom data
    public int level;
    public Vector3 position;
    // Existing fields...
}

No additional changes are required; JsonConvert.SerializeObject will capture the inventory list automatically.

Implementing Load Functionality

Loading a Specific Slot Programmatically

To load a specific save slot (e.g., "Data3") from a custom menu:

using UnityEngine;
using UCT.Core;

public class MyLoadButton : MonoBehaviour
{
    public void OnClick()
    {
        var player = SaveController.LoadData("Data3");
        if (player != null)
        {
            MainControl.Instance.playerControl = player;
            // Fact tables are restored automatically by LoadData
        }
        else
        {
            Debug.LogWarning("Save slot not found.");
        }
    }
}

This restores the PlayerControl state and all associated FactTables from Assets/Data/Data3.json.

Managing Save Files

The template includes utilities for save slot maintenance:

  • GetDataNumber – Counts existing save files.
  • DeleteData(string dataName) – Removes a specific save file.
  • SortAndRenameData – Renames remaining files to maintain a continuous sequence after deletion.

Deleting a Save Slot

using UCT.Core;

public class DeleteSaveSlot : MonoBehaviour
{
    public void RemoveSlot(int slotIndex)
    {
        SaveController.DeleteData($"Data{slotIndex}");
        // Add UI refresh logic here
    }
}

After deletion, SortAndRenameData ensures the remaining files are renamed to Data0.json, Data1.json, etc., preventing gaps in the numbering.

Summary

  • The Undertale‑Changer Template implements save/load via SaveController, SaveService, and SaveBoxController.
  • Save data is stored as JSON in Assets/Data/, with automatic file renaming handled by SortAndRenameData.
  • PlayerControl instances are serialized with JsonConvert.SerializeObject and restored with JsonConvert.PopulateObject.
  • FactTables are persisted alongside player data to maintain event system state.
  • Player preferences (language, resolution) are cached via PlayerPrefs through SaveService.

Frequently Asked Questions

Where are save files physically stored?

Save files are written to the Assets/Data/ directory within your Unity project as JSON files (e.g., Data0.json, Data1.json). This path is hardcoded in SaveController.SaveData, which ensures the directory exists before writing.

How do I add support for multiple save slots?

The system natively supports multiple slots. Simply pass a unique name (e.g., "Data0", "Data1") to SaveController.SaveData or SaveController.LoadData. The SortAndRenameData utility automatically renumbers files to maintain a continuous sequence when slots are deleted.

What happens to the event system state when I save?

The event system state is preserved via FactTables. SaveFactTablesToJson serializes all FactTable assets found in Resources/Tables, and LoadFactTablesFromJson restores them when loading. This ensures story flags and rule states persist across sessions.

Can I customize what gets saved without modifying the core system?

Yes. Extend the PlayerControl ScriptableObject with additional [Serializable] fields. Because SaveController.SaveData serializes the entire PlayerControl instance using JsonConvert.SerializeObject, any new fields you add will be automatically included in the JSON output without touching the save logic in SaveController.cs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →