# Saving your own components

`ISaveLoad` is the interface implemented by every save-system participant. Implement it on your own component or `ScriptableObject` when the type should save itself without a **Remember** component.

## The interface

This manager subscribes for its lifetime and saves one integer in a dedicated data class:

```cs
using System;
using UnityEngine;
using VisualScript.Runtime;

public class MyManager : MonoBehaviour, ISaveLoad
{
    [SerializeField] private SaveOptions m_SaveOptions = new SaveOptions();
    [SerializeField] private int m_Score;

    [Serializable]
    public class Data
    {
        [SerializeField] public int score;
    }

    private void Awake() => SaveLoadManager.Subscribe(this);
    private void OnDestroy() => SaveLoadManager.Unsubscribe(this);

    IdString ISaveLoad.Id => this.m_SaveOptions.Id;
    Type ISaveLoad.Type => typeof(Data);

    bool ISaveLoad.IsShared => false;
    bool ISaveLoad.IsPersistent => true;

    void ISaveLoad.OnReset() => this.m_Score = 0;

    object ISaveLoad.OnSave() => new Data { score = this.m_Score };

    Awaitable ISaveLoad.OnLoad(object value)
    {
        if (value is Data data) this.m_Score = data.score;
        return AwaitableUtils.DontAwait;
    }
}
```

`SaveOptions` supplies the stable ID, while `Data` defines the serialized contract. `Awake` and `OnDestroy` keep the manager registered only while it exists.

## The properties

These properties define which saved data belongs to the object and when the manager restores it:

| Property | Means |
| :--- | :--- |
| `Id` | Unique identity. Two objects with the same ID are treated as the same object. |
| `Type` | The type of the data returned by `OnSave`. |
| `Priority` | Restore order, higher first. Defaults to `0`. |
| `CanSave` | Whether to write at all. Defaults to `true`. |
| `IsShared` | `true` for data common to every profile, `false` for per-profile data. |
| `IsPersistent` | `true` if the object survives scene loads, `false` if it's scene-bound. |

### IsPersistent controls restore timing

Choose `IsPersistent` based on whether your object survives the scene load:

| Value | Restored | Suits |
| :--- | :--- | :--- |
| `true` | Before scenes load | Managers, singletons, assets |
| `false` | As its scene loads | Components on scene objects |

Marking a scene-bound object as persistent restores it before the scene load. The load then destroys the object and silently discards the restored state.

### Id must be stable

The manager uses the ID to match stored data to the object that receives it.

!!! warning "Never change an ID after shipping"
    Existing saves reference the old ID. Changing it makes the data unreachable, so the object returns with default state and no migration error.

The built-in `SaveOptions` field provides the same identity UI as the **Remember** component, including designer-set and generated IDs. Prefer it to a separate ID implementation.

## Subscribing

Subscribe when the object initializes and unsubscribe when its lifetime ends:

| Kind | Subscribe in | Unsubscribe in |
| :--- | :--- | :--- |
| `MonoBehaviour` | `Awake` | `OnDestroy` |
| `ScriptableObject` | Initialization | Never — assets outlive the session |

Forgetting to unsubscribe keeps a destroyed component in the subscriber list. The next save calls `OnSave` on that component.

## OnSave and OnLoad

`OnSave` returns any serializable object. Use a dedicated, flat data class to make the stored format explicit and keep component changes separate from save compatibility.

`OnLoad` receives it back as `object`. Always type-check rather than casting, because a save written by an older version may hold something different:

```cs
if (value is not Data data) return AwaitableUtils.DontAwait;
```

The type check returns immediately when the loaded value isn't `Data`. When the value is valid, `OnLoad` can await any required initialization before restoring it.

## OnReset

The save system calls `OnReset` when the game returns to a new-game state. Restore the component's authored defaults there.

Without this reset, state from the previous profile leaks into the new game.

## Versioning

Players may load saves written by an earlier version of your game. Two habits help preserve compatibility:

- **Add fields; do not repurpose them.** A new field receives its default in old saves, while a changed meaning corrupts the interpretation of existing data.
- **Include a version number** in the data class from the first release. It provides the branch needed for a later migration.

## Where to go next

- **[Custom memories](memories.md)** — capture components you do not own.
- **[Save and Load](../../runtime/save-load.md)** — call `SaveLoadManager` and observe its events.
- **[Save & Load](../../../manual/save-load/index.md)** — inspect the designer-facing lifecycle.
