Skip to content
Development
Skill

/save-system

Save/load patterns — ISaveable interface, JSON serialization, save file management, scene persistence, cloud sync prep. Load when implementing save functionality.

From plugin
everything-claude-unity
2442 skills20 agents27 commands
Install
$ npx -y skills add XeldarAlz/everything-claude-unity --skill save-system --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/save-system

Context preview

The summary Claude sees to decide when to auto-load this skill.

Save/load patterns — ISaveable interface, JSON serialization, save file management, scene persistence, cloud sync prep. Load when implementing save functionality.

SKILL.md

save-system.SKILL.md
name: save-system
description: "Save/load patterns — ISaveable interface, JSON serialization, save file management, scene persistence, cloud sync prep. Load when implementing save functionality."
globs: ["**/Save*.cs", "**/Load*.cs", "**/Persist*.cs", "**/Serializ*.cs"]

Save/Load System

Patterns for persisting game state to disk: an ISaveable interface for components that need persistence, a central SaveManager that orchestrates capture and restore, JSON serialization, save slot management, and preparation for cloud sync.

ISaveable Interface

Every component that needs to save state implements this interface. The SaveManager discovers all ISaveable objects in the scene and calls them during save/load.

/// <summary>
/// Implement on any MonoBehaviour that needs to persist state across saves.
/// </summary>
public interface ISaveable
{
    /// <summary>
    /// Unique key for this saveable. Must be stable across sessions.
    /// Recommended format: "{scene}_{gameobject}_{component}" or a GUID.
    /// </summary>
    string SaveKey { get; }

    /// <summary>
    /// Capture current state as a serializable object.
    /// Return a plain C# class or struct (no MonoBehaviour, no ScriptableObject).
    /// </summary>
    object CaptureState();

    /// <summary>
    /// Restore state from a previously captured object.
    /// Cast the object to the expected type.
    /// </summary>
    void RestoreState(object state);
}

Example: Saveable Health Component

using UnityEngine;

public class Health : MonoBehaviour, ISaveable
{
    [SerializeField] private int maxHealth = 100;
    [SerializeField] private string saveKey;

    private int _currentHealth;

    public string SaveKey => saveKey;

    private void Awake()
    {
        _currentHealth = maxHealth;
    }

    [System.Serializable]
    private struct HealthSaveData
    {
        public int currentHealth;
        public int maxHealth;
    }

    public object CaptureState()
    {
        return new HealthSaveData
        {
            currentHealth = _currentHealth,
            maxHealth = maxHealth
        };
    }

    public void RestoreState(object state)
    {
        if (state is HealthSaveData data)
        {
            _currentHealth = data.currentHealth;
            maxHealth = data.maxHealth;
        }
    }
}

Generating Stable Save Keys

The save key must be the same every time the game runs. Options:

1. **Manual string** (simplest): Assign in Inspector. Works for unique objects like "player_health". 2. **GUID component:** Add a `SaveableEntity` MonoBehaviour with a `[SerializeField] private string uniqueId` that generates a GUID in `Reset()` (called when the component is first added in the editor). This auto-generates stable IDs.

using UnityEngine;

public class SaveableEntity : MonoBehaviour
{
    [SerializeField] private string uniqueId;

    public string UniqueId => uniqueId;

    // Called in editor when component is first added
    private void Reset()
    {
        uniqueId = System.Guid.NewGuid().ToString();
    }
}

---

Save Data Structure

A single save file contains all captured state, plus metadata.

using System;
using System.Collections.Generic;

[Serializable]
public class SaveData
{
    public int saveVersion = 1;
    public string timestamp;
    public string sceneName;
    public float playTime;

    // All saveable state, keyed by ISaveable.SaveKey
    // Values are JSON strings (serialized individually per saveable)
    public Dictionary<string, string> stateEntries = new();
}

Using `Dictionary<string, string>` where values are JSON strings (rather than `Dictionary<string, object>`) avoids polymorphic serialization issues with `JsonUtility`. Each ISaveable's state is serialized independently.

---

Save Manager

The central orchestrator. Finds all ISaveable components, serializes their state, and writes to disk.

using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SaveManager : MonoBehaviour
{
    public static SaveManager Instance { get; private set; }

    [SerializeField] private int maxSaveSlots = 3;

    private float _sessionStartTime;

    public event Action OnSaveCompleted;
    public event Action OnLoadCompleted;

    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);
        _sessionStartTime = Time.time;
    }

    // --- File Paths ---

    private string GetSaveFolderPath()
    {
        return Path.Combine(Application.persistentDataPath, "Saves");
    }

    private string GetSaveFilePath(int slot)
    {
        return Path.Combine(GetSaveFolderPath(), $"Save{slot}.json");
    }

    private string GetAutoSaveFilePath()
    {
        return Path.Combine(GetSaveFolderPath(), "AutoSave.json");
    }

    // --- Save ---

    public void Save(int slot)
    {
        SaveToFile(GetSaveFilePath(slot));
    }

    public void AutoSave()
    {
        SaveToFile(GetAutoSaveFilePath());
    }

    private void SaveToFile(string path)
    {
        var saveData = new SaveData
        {
            saveVersion = 1,
            timestamp = DateTime.Now.ToString("o"),
            sceneName = SceneManager.GetActiveScene().name,
            playTime = Time.time - _sessionStartTime
        };

        // Find all saveables in the scene
        var saveables = FindAllSaveables();

        foreach (var saveable in saveables)
        {
            try
            {
                object state = saveable.CaptureState();
                string json = JsonUtility.ToJson(state);
                saveData.stateEntries[saveable.SaveKey] = json;
            }
            catch (Exception e)
            {
                Debug.LogError($"Failed to capture state for {saveable.Sa
Read more
Ships witheverything-claude-unity

The ultimate Claude Code toolkit for Unity game development. A production-ready, plug-and-play system that gives Claude Code deep Unity expertise — from writing performant C# to building scenes, profiling performance, and triggering iOS/Android builds — all

Get the whole plugin

Other skills on everything-claude-unity.