Features of the Save System in the Undertale Changer Template

TLDR: The Undertale Changer Template provides a complete slot-based save system with JSON serialization, automatic fact-table persistence, sequential slot management, and an integrated in-game UI with pause functionality.

The Undertale Changer Template ships with a production-ready save architecture designed for Unity-based Undertale-style games. According to the source code in arch-aik/undertale-changer-template, the system handles player data persistence, narrative state tracking, and user preferences through a centralized JSON-based workflow. Understanding these features allows developers to customize save behaviors without rebuilding core infrastructure.

JSON-Based Player Data Persistence

At the core of the Undertale Changer Template save system is the SaveController.SaveData method in Assets/Scripts/UCT/Core/SaveController.cs. This method serializes the PlayerControl ScriptableObject to JSON using Newtonsoft.Json and writes it to Application.dataPath/Data/*.json.

The implementation automatically creates the Data folder if missing and generates files following the Data{n}.json naming convention. This ensures that player progress, inventory, and positional data persist between sessions using human-readable JSON files rather than binary formats.

Automatic Fact-Table Serialization

Beyond player stats, the system preserves narrative state through FactTable assets. The SaveFactTablesToJson method scans the Resources/Tables directory and serializes all table assets to a companion file named <saveName>Table.json.

This feature stores the path-to-fact mapping alongside player data, ensuring that story flags, dialogue states, and progression markers synchronize with the specific save slot. When loading, LoadFactTablesFromJson reconstructs these tables to restore the exact narrative context.

Sequential Slot Management and Auto-Renaming

The template enforces clean slot organization through the SortAndRenameData method. When players delete saves or create new ones, the system automatically reorders files to eliminate gaps, ensuring sequential naming from Data0.json upward.

This SortAndRenameData implementation (lines 92-134) handles both player data files and their corresponding table companions simultaneously. The GetDataNumber method provides a quick count of existing slots by scanning for Data*.json patterns, enabling dynamic UI generation for save/load screens.

Loading, Deletion, and Data Recovery

Loading operations deserialize JSON back into fresh PlayerControl instances via SaveController.LoadData. This method reads the specified slot file, populates a new ScriptableObject, caches it for performance, and triggers fact-table restoration.

For deletion, DeleteData removes the specified slot file and its cached entry before invoking the renaming pass to close sequence gaps. This ensures that the slot inventory remains compact and predictable after save removal operations.

In-Game Save UI and Pause Integration

The SaveBoxController class in Assets/Scripts/UCT/Overworld/SaveBoxController.cs provides the visual interface and input handling layer. When activated via OpenSaveBox, the system pauses the game by disabling player movement and setting a global pause flag through SetGamePaused.

Input handling supports left/right arrow keys to toggle between Confirm and Cancel options, while Z confirms selections and X cancels. The HandleSelectionInput, HandleConfirmationInput, and HandleCancellationInput methods manage these interactions, providing immediate visual feedback through the UI elements.

PlayerPrefs Integration for Settings

The save system extends beyond game state to include user preferences. The SaveService.SavePlayerPreferences method persists language settings, resolution, last used slot, SFX, and V-Sync configurations to PlayerPrefs. This ensures that display and audio preferences persist independently of individual save slots, maintaining consistency across playthroughs.

How to Use the Save System

Developers interact with the Undertale Changer Template save system through straightforward API calls that wrap the underlying complexity.

Saving the Game

To trigger a complete save operation including player data, scene information, and preferences:

// Centralized save call that handles all persistence layers
SaveService.SaveGame();

This method, located in SaveBoxController.cs, coordinates SaveController.SaveData, scene metadata capture, and preference persistence in a single atomic operation.

Loading a Specific Slot

To load existing save data programmatically:

// Load slot 2 (corresponds to Data2.json)
PlayerControl loadedPlayer = SaveController.LoadData("Data2");
if (loadedPlayer != null)
{
    // Apply loaded data to current game state
}

The LoadData method returns a populated PlayerControl instance ready for injection into the active game scene.

Deleting Save Data

To remove a specific slot and maintain sequential ordering:

// Delete slot 3 and auto-rename remaining files
SaveController.DeleteData("Data3");

This operation removes both the player data file and its associated fact-table companion before triggering the automatic renaming pass.

Querying Available Slots

To determine how many save slots exist for UI generation:

int slotCount = SaveController.GetDataNumber();
// Returns 4 if Data0.json through Data3.json exist

This scan-based approach allows dynamic save menu construction that reflects the current Data folder contents.

Summary

  • The Undertale Changer Template provides JSON-based serialization for PlayerControl data via SaveController.SaveData in Assets/Scripts/UCT/Core/SaveController.cs.
  • Fact-table persistence automatically synchronizes narrative state files alongside player saves using companion JSON files.
  • Auto-renaming logic maintains sequential slot ordering without gaps through SortAndRenameData, called automatically during save and delete operations.
  • The in-game UI layer in SaveBoxController.cs handles input routing, game pausing, and visual feedback with dedicated methods for confirmation and cancellation.
  • Player preferences persist separately via PlayerPrefs through the SaveService integration, storing settings like language and resolution independently of game saves.

Frequently Asked Questions

How does the Undertale Changer Template handle save file organization?

The template maintains sequential slot naming (Data0.json, Data1.json, etc.) through the SortAndRenameData method. When saves are deleted, the system automatically renames remaining files to eliminate gaps, ensuring that GetDataNumber always returns a continuous count. Player data and fact-table companions are renamed in tandem to maintain data integrity.

What data is included when saving in the Undertale Changer Template?

Each save captures the complete PlayerControl object (position, inventory, stats) as JSON, plus all FactTable assets from Resources/Tables. Additionally, SaveService.SavePlayerPreferences stores language, resolution, audio, and V-Sync settings to PlayerPrefs. Scene metadata including current level and player position is also recorded.

Can I modify the save UI behavior in the Undertale Changer Template?

Yes. The SaveBoxController class in Assets/Scripts/UCT/Overworld/SaveBoxController.cs exposes methods like OpenSaveBox, SetGamePaused, and UpdateAfterSaveUI for customization. Input handling logic resides in HandleSelectionInput and related methods, allowing developers to remap keys or change the confirmation flow. The UI prefab elements (saveHeart, saveText) can be configured via the BackpackBehaviour prefab reference.

Where are save files stored in the Undertale Changer Template?

Save files are written to Application.dataPath/Data/ as individual JSON files following the Data{n}.json pattern, with companion files Data{n}Table.json for fact tables. This places saves in the application's data directory, which varies by platform but ensures accessibility for debugging and backup purposes.

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 →