ScriptableObject Perfect Guide

 ”A ScriptableObject is a data container that you can use to save large amounts of data, independent of class instances.”*1 It improve project’s performance. When your project have some similar scripts like managing a enemy and environment, those parameter create each instance in memory. If those refer to a variable in ScripatableObject, it reduce instance to one.

 ScriptableObject have other usage besides optimizing projects. It’s just “.asset” file having game data but you can also use it like as Json and CSV. ScriptableObject store it as unity law data, so the processing speed is fast. In addition, you can set a value in Inspecter, so it’s easy to cange and see it. When a project refer to a same instance across scenes, it isn’t  destroyed and can be used  like static variable.

 I introduce basic usage of ScriptableObject and the way to use it in my game! If you want sample, please check my Github project!

 

Basis 

 At first, you create a script inheriting SctiptableObject instead of MonoBehaviour. If you give it’s class “CreateAssetMenu” attribute, you can make asset file on menu of the inspector. You decide a file name , a name on menu and  an order on create menu with this attribute. This is all you need to use ScriptableObject. You can create it from “Create” on Project window or “Assets/Create” on window menu.

using UnityEngine;
[CreateAssetMenu(fileName = "Data",
 menuName = "GameData/EnemyData",
order = 0)]
public class ScriptableContainer : ScriptableObject {
 public enum EnemyType {
 Nomal,
 Boss
}

public EnemyType enemyType;
public int enemyLevel;
public bool isHuman;
public Vector3[] spawnPoints;
}

 When you use create one, you attach it to a script on inspecter and get it from Resources by its name. At this, that variable name should be ScripableObject’s name instead of a assets file’s.

ScriptableContainer scriptableContainer;
  How is SctiptableObject performance?

  ScriptableObject is high performance in loading speed and memory. This is because ScriptableObject store unity raw data.

Size Load Memory
JSON(Uncompressed)
JSON(Compressed)
ScriptableObject

Comparison with  ScriptableObject and JSON.*2 

Edit return value

 ScriptableObject can use function and property.  So it means to edit return value. When you develop a game with a team and other people set the data,  specified value  on editor may be easy to understand. 

 This sample is for Item data. You can set that type and it change id automatically. This sample give one value as ID but adding it by Rarity can create many variety ID.

using UnityEngine;

[CreateAssetMenu(fileName = "Data", menuName = "GameData/ItemData", order = 1)]
public class ScriptableProperty : ScriptableObject {
    public enum ItemType {
        Fish,
        Meat,
        Mushroom
    }
    public enum ItemRarity {
        Common,
        Rare,
        Epic
    }

    [SerializeField] ItemType _itemType;
    [SerializeField] ItemRarity _itemRarity;
    public int stock;
    int _itemID;
    public int ItemID {
        get { return _itemID; }
        private set {  _itemID = value;}
    }
    public string ItemName { get { return _itemType.ToString() + _itemRarity.ToString(); } }
    

    void SetID() {
        if (_itemType == ItemType.Fish) {
            ItemID = 101;
        } else if (_itemType == ItemType.Meat) {
            ItemID = 201;
        } else if (_itemType == ItemType.Mushroom) {
            ItemID = 301;
        }
    }
    void Start() {
        SetID();
    }
}

 And the result is like this.

using UnityEngine;

public class ScriptableTest : MonoBehaviour {
    [SerializeField] ScriptableProperty itemData;

    void Start() {
        Debug.Log("ID:" + itemData.ItemID + "/Name:" + itemData.ItemName + "/Stock:" + itemData.stock);
    }
}

 

Make other data construction in ScripatableObject

 ScriptableObject can have other data construction. When it have other structure, it need serialize the part explicitly. If you don’t add serialize attribute, the data doesn’t appear on inspector. *3 If the first data of an construction is string, each array name become that character.

 This example is for story data. It hold the whole data of one scene. And it get some data from Resources.

using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(menuName = "GameData/CreateProt")]
public class StoryData : ScriptableObject {

    enum BGM {
        Street,
        GrassLand,
        NightPark
    }
    [SerializeField] BGM _bgm;
    public string bgm { get { return _bgm.ToString(); } }
    public List StoryScript = new List();
}

[System.Serializable]
public class StoryScene {
    enum TextRole {
        Title,
        Discription,
        Conversation
    }
    //Scene background
    enum Field {
        House,
        Street,
        Grassland
    }

    enum Model {
        David,
        Thijs
    }

    enum ModelPos {
        Right,
        Center,
        Left
    }
    public string Speaker;

    [SerializeField] TextRole _textRole;
    public int textRole { get { return (int)_textRole; } }

    [SerializeField] Model _model;
    public int ModelNumber; //Change the model image.
    public string model { get { return _model.ToString() + ModelNumber.ToString(); } }


    [SerializeField] ModelPos _modelPos; //Change the model position by if statement.
    public int modelPos { get { return (int)_modelPos; } }

    [SerializeField] Field _field;
    [SerializeField] int fieldNumber;
    public string field { get { return _field.ToString() + fieldNumber.ToString(); } }

    public string SceneText;
}
  It’s an example for loading above ScripatableObject. At first it get the whole data and load each one. 
 This is the result loding the data. After reading all data, it invoke the end process.
using System.Collections.Generic;
using UnityEngine;

public class SampleRoder : MonoBehaviour {
    StoryData storyData;
    StoryScene storyScene;
    int loadLine, storynumber;

    void Start() {
        storynumber = 0;
        //Get an asset file from resources.
        //Set the "storynumber" on other scene.
        storyData = Resources.Load("Story" + storynumber.ToString() ) as StoryData;
        LoadNextPart(); 
    }

    //Subscribe to button click event.
    public void LoadNextPart() {

        if (loadLine < storyData.StoryScript.Count) {
            //At first, load some data from array.
            storyScene = storyData.StoryScript[loadLine];
            //Do stuff here
            
            Debug.Log(storyScene.SceneText);

            loadLine++;
        } else {
            DisplayResult();
        }
    }

    void DisplayResult() {
        Debug.Log("CLEAR!");
    }
}
 

Notes

 The value of ScriptableObject remain on the editor like Transform value. However, in an actual equipment the values are initialized every time player start app. So you use ScriptableObject like an actual environment, need to initialize the values on Start or OnEnable.

 

Reference 

1. Unity – Manual: ScriptableObject
2. ScriptableObjectとJSONの比較(データサイズ、ロード時間、メモリ使用量)【Unity】【ScriptableObject】 – (:3[kanのメモ帳]
3.Unity – Manual: Script Serialization