1. 程式人生 > 實用技巧 >【Unity】ScriptableObject

【Unity】ScriptableObject

【Unity】ScriptableObject的介紹 —— CSDN

簡述

最近對unity提供的ScriptableObject比較感興趣,想了解一下這方面的技術知識。故此參考網上的學習資料總結一遍。

ScriptableObject的好處

  1. 將遊戲資料儲存在了資原始檔中,能夠在遊戲退出後依然保持這一份遊戲資料。
  2. 能依賴繼承實現很好的複用關係。

個人覺得第一點是非常好的理念,讓遊戲的資料儲存變得更加的優雅便捷。同時能夠實現高複用,來適應多個不同的使用場景。

解決的問題

  • 執行時為了儲存資料,基本依靠的是Copy Component Value來解決,非常的麻煩,如果存在多個數據想要儲存的話基本沒辦法。這種時候就應該考慮使用ScriptableObject。
  • 在場景和專案之間很難實現共享,基本也是要序列化一個新的資原始檔來實現。
  • 在概念上難以定義這種物件,儲存的資料內容不能很好的作區分。擴充套件性不強。

缺點

  • 回撥函式較少
    • OnEnable
    • OnDisable
    • OnDestroy
  • 真正意義上的共享,因此如果修改了資料,就真的修改了

使用方式

  1. 繼承自MonoBehavior的改成ScriptabObject
using UnityEngine;

[CreateAssetMenu(menuName="MySubMenue/Create MyScriptableObject ")]
public class MyScriptableObject : ScriptableObject
{
    public int someVariable;
}

CreateAssetMenu會在資源選單建立一個ScriptaObject的選項,方便建立物件。

回撥函式的呼叫時機

  • OnEnable
    • 建立的時候
    • 載入的時候
    • 過載程式碼的完成時候
  • OnDisable
    • 開始過載程式碼的時候
    • 將要銷燬的時候
  • OnDestroy
    • 銷燬時

參考例子——遊戲設定

遊戲設定繼承自ScriptableObject,並實現單例模式。這樣子在設定中去呼叫GetInstance方法可以就可以訪問到當前的遊戲設定。而且退出遊戲時遊戲資料也繼續保持。

[CreateAssetMenu]
public class GameSettings : ScriptableObject
{
    [Serializable]
    public class PlayerInfo
    {
        public string Name;
        public Color Color;

        ...
    }

    public List<PlayerInfo> players;

    private static GameSettings _instance;
    public static GameSettings Instance
    {
        get
        {
            if (!_instance)
                _instance = Resources.FindObjectsOfTypeAll<GameSettings>().FirstOrDefault();
#if UNITY_EDITOR
            if (!_instance)
                InitializeFromDefault(UnityEditor.AssetDatabase.LoadAssetAtPath<GameSettings>("Assets/Test game settings.asset"));
#endif
            return _instance;
        }
    }

    public int NumberOfRounds;

    public static void LoadFromJSON(string path)
    {
        if (!_instance) DestroyImmediate(_instance);
        _instance = ScriptableObject.CreateInstance<GameSettings>();
        JsonUtility.FromJsonOverwrite(System.IO.File.ReadAllText(path), _instance);
        _instance.hideFlags = HideFlags.HideAndDontSave;
    }

    public void SaveToJSON(string path)
    {
        Debug.LogFormat("Saving game settings to {0}", path);
        System.IO.File.WriteAllText(path, JsonUtility.ToJson(this, true));
    }

    public static void InitializeFromDefault(GameSettings settings)
    {
        if (_instance) DestroyImmediate(_instance);
        _instance = Instantiate(settings);
        _instance.hideFlags = HideFlags.HideAndDontSave;
    }

#if UNITY_EDITOR
    [UnityEditor.MenuItem("Window/Game Settings")]
    public static void ShowGameSettings()
    {
        UnityEditor.Selection.activeObject = Instance;
    }
#endif

    ...
}
public class MainMenuController : MonoBehaviour
{
    public GameSettings GameSettingsTemplate;

    ...

    public string SavedSettingsPath {
        get {
            return System.IO.Path.Combine(Application.persistentDataPath, "tanks-settings.json");
        }
    }

    void Start () {
        if (System.IO.File.Exists(SavedSettingsPath))
            GameSettings.LoadFromJSON(SavedSettingsPath);
        else
            GameSettings.InitializeFromDefault(GameSettingsTemplate);

        foreach(var info in GetComponentsInChildren<PlayerInfoController>())
            info.Refresh();

        NumberOfRoundsSlider.value = GameSettings.Instance.NumberOfRounds;
    }

    public void Play()
    {
        GameSettings.Instance.SaveToJSON(SavedSettingsPath);
        GameState.CreateFromSettings(GameSettings.Instance);
        SceneManager.LoadScene(1, LoadSceneMode.Single);
    }

    ...
}