Saving/Loading Scriptable Object in Unity

Scriptable object

What is a Scriptable object?

According to Unity, “A ScriptableObject is used to save large amounts of shared data, independent of class instances”. So basically it is used to save predefined static data at the editing time and can be used at runtime. However, note that the Scriptable object and Monobehavior Object, both are different.

It is a special kind of object in unity that only to be used to store data. While Monobehavior Gameobject will be used in the scene and have the default Transform component. It can not be used as any other component in unity.

It also helps us to reduce memory usage and get better performance, it is like a pluggable data set. An example of this would be to imagine a Gun Inventory in Shooting Multiplayer Game. You could create multiple assets of each gun with an intentional gun property. Each gun has a different but static property on each level, so it could be helpful to store gun properties for an independent level.

How to create a scriptable object?

Let’s think of a game having an avatar selection screen. All avatar has its own data. So create a class called Avatar.cs by following,

using UnityEngine;

[System.Serializable]
public class Avatar
{
    public string AvatarName;
    public byte AvatarId;
    public Sprite Icon;
    public string Description;
}

Now let us create a list of Avatar Class as a Scriptable object.

using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "Avatar Data", menuName = "Create Avatar Data", order = 51)]
public class AvatarData : ScriptableObject
{
    public List<Avatar> avatars = new List<Avatar>();
}

If everything went right, you should be able to go to Assets->Create and see Avatar Data asset in the asset menu. Click on it will create an AvatarData.asset file in the Asset folder in Unity. You can even create it by right click on the project window.

Scriptable object creation

Now, it is time to fill the data in the asset we’ve just created. You can fill any amount of data in this list.

Scriptable object data

How to use Scriptable Data with Monobehaviour?

We can use the scriptable object as a variable in Monobehaviour script and expose its data as our requirement. Now create an empty game object and apply the below script to it.

using UnityEngine;

public class AvatarSelection : MonoBehaviour
{
    [SerializeField]
    private AvatarData avatarData;

    private void Start()
    {
        for (int i = 0; i < avatarData.avatars.Count; i++)
        {
            Debug.Log("Avatar Name: " + avatarData.avatars[i].AvatarName);
            Debug.Log("Avatar Id: " + avatarData.avatars[i].AvatarId);
            Debug.Log("Avatar description: " + avatarData.avatars[i].Description);
        }
    }
}

You can Assign the avatarData variable from the inspector window.

Assign Monobhaviour variable

Till now we use data statically. If we changed something in runtime it will actually remember this. But what can we do if we want to save and load a scriptable object from the file?.

Now first move our scriptable object asset into Resources folder to load it as a default avatarData. After moving the asset, add the following lines of code to your Monobehaviour script to save the data to file in persistentDataPath in unity.

void SaveData()
{
   string json = JsonUtility.ToJson(avatarData);
   File.WriteAllText(Application.persistentDataPath + Path.DirectorySeparatorChar + "AvatarData.txt", json);
}

Use this function to save the avatarData in JSON format in the file. Momentarily We will load this data from the file and assign to our AvatarData variable. Later using this code, you no longer require to assign avatarData variable from the inspector.

AvatarData LoadData()
{
        AvatarData data = null;

        if (File.Exists(Application.persistentDataPath + Path.DirectorySeparatorChar + "AvatarData.txt"))
        {
            data = ScriptableObject.CreateInstance<AvatarData>();
            string json = File.ReadAllText(Application.persistentDataPath + Path.DirectorySeparatorChar + "AvatarData.txt");
            JsonUtility.FromJsonOverwrite(json, data);
        }
        else
        {
            data = Resources.Load<AvatarData>("Avatar Data");
        }
        return data;
}

Final code will be looking like this.

using System.IO;
using UnityEngine;

public class AvatarSelection : MonoBehaviour
{
    private AvatarData avatarData;

    private void Awake()
    {
        avatarData = LoadData();
    }

    private void Start()
    {
        for (int i = 0; i < avatarData.avatars.Count; i++)
        {
            Debug.Log("Avatar Name: " + avatarData.avatars[i].AvatarName);
            Debug.Log("Avatar Id: " + avatarData.avatars[i].AvatarId);
            Debug.Log("Avatar description: " + avatarData.avatars[i].Description);
        }
    }

    private void OnDisable()
    {
        SaveData();
    }

    void SaveData()
    {
        string json = JsonUtility.ToJson(avatarData);
        File.WriteAllText(Application.persistentDataPath + Path.DirectorySeparatorChar + "AvatarData.txt", json);
    }

    AvatarData LoadData()
    {
        AvatarData data = null;

        if (File.Exists(Application.persistentDataPath + Path.DirectorySeparatorChar + "AvatarData.txt"))
        {
            data = ScriptableObject.CreateInstance<AvatarData>();
            string json = File.ReadAllText(Application.persistentDataPath + Path.DirectorySeparatorChar + "AvatarData.txt");
            JsonUtility.FromJsonOverwrite(json, data);
        }
        else
        {
            data = Resources.Load<AvatarData>("Avatar Data");
        }
        return data;
    }
}

In conclusion, We understand this procedure to save and load a scriptable object at runtime. It is very beneficial when we have a large amount of data with an identical pattern. We can scatter this data into chunks and use it as a scriptable object.

Leave a Reply