> For the complete documentation index, see [llms.txt](https://infinitypbr.gitbook.io/magic-pig-games/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://infinitypbr.gitbook.io/magic-pig-games/game-modules-4/module-documentation/save-and-load/making-objects-saveable.md).

# Making objects saveable

Objects can automatically save and load their data once the method is started on the Save and Load module.

To make an object saveable, first add the `Savable` component to them.

<figure><img src="/files/sPPLiKBLMnJjhvFUVDNg" alt=""><figcaption></figcaption></figure>

The component will automatically generate a Save ID, which is unique to this object. This value must be unique among all of the Saveable objects.

{% hint style="warning" %}
Once set, the Save ID should \*NEVER\* change. If the Save ID changes, data will not be loaded into the object!

I've added the giant "Unlock" button to help ensure you do not accidentally change this value. If you'd like to change it, click "Unlock" first.
{% endhint %}

Next, make sure the classes on the object which need to be saved implements `ISaveable`.

`ISaveable` requires the following methods:

```
public string SaveableObjectId();

public object SaveState();
public void LoadState(string jsonEncodedState);
        
public void PreSaveActions();
public void PostSaveActions();

public void PreLoadActions();
public void PostLoadActions();
```

The Pre- and Post- actions are optional. While you'll have to add them to your class, you may choose to not use them for any purpose.

## SaveableObjectId()

The SaveableObjectId() can be randomly generated by your script or you can set it to somethign more human readable. There should only be one class with a specific Saveable Object Id on each Saveable object. However, the same Saveable Object Id can live on multiple different Saveable objects.

The saved data is stored in a structure based on teh Save ID & Saveable Object ID combined.

```csharp
// Use a specific Saveable Object Id
[SerializeField] private string _saveableObjectId = "Game Data";
public string SaveableObjectId() => _saveableObjectId;

// Generate a random Saveable Object Id
// If the value of _saveableObjectId is not set, we will create a new random string
// and set it. Otherwise, we will return the value.
[SerializeField] private string _saveableObjectId;
public string SaveableObjectId() 
    => string.IsNullOrWhiteSpace(_saveableObjectId) 
        ? _saveableObjectId = GUID.Generate().ToString() 
        : _saveableObjectId;
```

{% hint style="success" %}
**Use the AI to speed up development**

Did you know if you copy/paste the documentation below into ChatGPT, and then paste in your struct, it can make the SaveState() and LoadState() methods for you?

That should save you a good 5 minutes :)

[Here's an example.](https://chat.openai.com/share/00d7e086-fd9b-4aa2-9ab2-49f62b3b40fa)
{% endhint %}

## `SaveState()` and `LoadState()`

You will need to add a new `struct` to contain the saved data. In this you will specify which data in your class should be saved. Note that only data types which can be serialized can be saved. Here is the `struct` from the `GameData` object in the **Party Based RPG demo game**, which can be found in `GameData.cs`.

```csharp
[System.Serializable]
public struct GameDataSaveData
{
    public string uid;
    public string gameName;
    public GameStatList partyStats;
    public List<DemoActor> players;
    public int activePlayerIndex;
    public Vector3 position;
    public Quaternion rotation;
    public GameQuestList quests;
}
```

#### `SaveState()`

In your `SaveState()` method, you will create a new data object from your `struct`, and populate it with the current values. The method will return the data object to the `Saveable` class, which will combine it with all the other classes with saved data, encode it, and send it to the **Save and Load** module.

You can do more than just what is displayed below, of course, by adding additional code to the `SaveState()` method.

```csharp
public object SaveState()
{
    var data = new GameDataSaveData
    {
        uid = Uid(),
        gameName = _gameName,
        partyStats = partyStats,
        players = players,
        activePlayerIndex = ActivePlayerIndex,
        position = PlayerObjectPosition,
        rotation = PlayerObjectRotation,
        quests = quests
    };
    
    return data;
}
```

#### LoadState()

Your LoadState() method will do the opposite of SaveState(). A JSON encoded `string` will be provided, and your method will need to decode the `string` into the `struct` you've created, and then populate the values in your class with the values from the decoded object.

{% hint style="info" %}
In the example below, since the `GameData` class is a singleton, the `GameDataIsSetButIsNotThis()` method will return true if this instance of `GameData` is not the registered singleton, and skip loading, avoiding a potential error.

After the data is loaded, we also call `StartActions()`, a method which sets up objects in the scene.

These are examples of additional logic you can add into your save and load methods.
{% endhint %}

```csharp
public void LoadState(string jsonEncodedState)
{
    // If this is not the Singleton GameData, we do nothing.
    if (GameDataIsSetButIsNotThis())
        return;
           
    // Decode the JSON data into a GameDataSaveData object
    var data = JsonUtility.FromJson<GameDataSaveData>(jsonEncodedState);
            
    // Set the values from data to the GameData object
    _uid = data.uid;
    _gameName = data.gameName;
    partyStats = data.partyStats;
    players = data.players;
    ActivePlayerIndex = data.activePlayerIndex;
    quests = data.quests;

    // Do the StartActions()
    StartActions();
}
```

{% hint style="warning" %}
If you are using a **Character Controller**, you likely will need to disable it prior to setting your **Transform** values, then enable it right after. Otherwise, it will block your object from moving, if it's already live in the scene.

Here's an example of my `LoadState()` method on the "Party" object in [**Legend of the Stones**](/magic-pig-games/legend-of-the-stones/actors-players-npcs.md). This object is the in-world representation of the player with the camera etc.

```
public void LoadState(string jsonEncodedState) { var data = JsonUtility.FromJson(jsonEncodedState);
        CharacterController.enabled = false;
        this.actor = data.actor;
        Transform.position = data.position;
        Transform.rotation = data.rotation;
        CharacterController.enabled = true;
        
        // Add any additional logic post-loading if needed
}
```

{% endhint %}
