1. 程式人生 > >【Unity編輯器擴充套件簡介】儲存資料

【Unity編輯器擴充套件簡介】儲存資料

unity編輯器中有三種方式可以儲存資料。

EditorPrefs

EditorPrefs可以在PC中儲存共享資料,即不受制於專案地儲存資訊

影響範圍

儲存的值會影響每一個大版本的Unity編輯器
以前儲存在Unity4.x的值只能在Unity 4.x版進行處理,Unity5.x的還可以在Unity 5.x中只處理。

這裡寫圖片描述

儲存什麼

應在EditorPrefs儲存的視窗的位置和大小,Unity編輯器結構的值等。通過EditorPrefs儲存的所有值都以文字形式儲存,所以密碼等重要資訊,請不要儲存。

EditorPrefs儲存的位置

平臺位置
Windows(Unity4.x) HKEY_CURRENT_USER\Software\Unity Technologies\UnityEditor 4.x
Windows(Unity5.x) HKEY_CURRENT_USER\Software\Unity Technologies\UnityEditor 5.x
Mac OS X(Unity4.x) ~/Library/Preferences/com.unity3d.UnityEditor4.x.plist
Mac OS X(Unity5.x) ~/Library/Preferences/com.unity3d.UnityEditor5.x.plist

EditorPrefs為每個主要版本單獨儲存。Windows將儲存在登錄檔中。如果在Windows通過EditorPrefs儲存值,如果出錯可能會使Windows無法啟動。

這裡寫圖片描述

當Enable的時候獲取儲存過的值,在intervalTime改變的時候,儲存intervalTime

using UnityEngine;
using UnityEditor;

public class ExampleWindow : EditorWindow
{
    int intervalTime = 60;
    const string AUTO_SAVE_INTERVAL_TIME = "AutoSave interval time (sec)";


    [MenuItem ("Window/Example")]
    static void Open ()
    {
        GetWindow <ExampleWindow> ();
    }

    void
OnEnable () { intervalTime = EditorPrefs.GetInt (AUTO_SAVE_INTERVAL_TIME, 60); } void OnGUI () { EditorGUI.BeginChangeCheck (); //シーン自動儲存間隔(秒) intervalTime = EditorGUILayout.IntSlider ("間隔(秒)", intervalTime, 1, 3600); if (EditorGUI.EndChangeCheck ()) EditorPrefs.SetInt (AUTO_SAVE_INTERVAL_TIME, intervalTime); } }

另外,視窗的尺寸這種重要性不高的值在OnDisable的時候儲存比較適合。千萬不要在OnGUI中每次儲存,OnGUI會被多次呼叫,負荷很高。

using UnityEngine;
using UnityEditor;

public class ExampleWindow : EditorWindow
{
    const string SIZE_WIDTH_KEY = "ExampleWindow size width";
    const string SIZE_HEIGHT_KEY = "ExampleWindow size height";

    [MenuItem ("Window/Example")]
    static void Open ()
    {
        GetWindow <ExampleWindow> ();
    }

    void OnEnable ()
    {
        var width = EditorPrefs.GetFloat (SIZE_WIDTH_KEY, 600);
        var height = EditorPrefs.GetFloat (SIZE_HEIGHT_KEY, 400);
        position = new Rect (position.x, position.y, width, height);
    }

    void OnDisable ()
    {
        EditorPrefs.SetFloat (SIZE_WIDTH_KEY, position.width);
        EditorPrefs.SetFloat (SIZE_HEIGHT_KEY, position.height);
    }
}

EditorUserSettings.Set / GetConfigValue

儲存可在專案內共享資料的方法。這裡儲存的值將進行加密,適合於儲存諸密碼等個人資訊。

儲存什麼

可以用EditorUserSettings來儲存使用者名稱和密碼,OAuth訪問令牌等。

EditorUserSettings.asset是以二進位制形式儲存的。但使用binary2text還是可以看到內容的。

儲存在哪裡

儲存在專案的Libray目錄下的EditorUserSettings.assets中

如何使用

using UnityEditor;

public class NewBehaviourScript
{
    [InitializeOnLoadMethod]
    static void SaveConfig ()
    {
        EditorUserSettings.SetConfigValue ("Data 1", "text");
    }
}

為了確保資料被儲存,我們可以進入目錄檢視

CD /Applications/Unity/Unity.app/Contents/Tools
./binary2text /path/to/unityproject/Library/EditorUserSettings.asset

可以看到已經儲存成功了:

External References


ID: 1 (ClassID: 162) EditorUserSettings
    m_ObjectHideFlags 0 (unsigned int)
    m_ConfigValues  (map)
        size 2 (int)
        data  (pair)
            first "Data 1" (string)
            second "17544c12" (string)
        data  (pair)
            first "vcSharedLogLevel" (string)
            second "0a5f5209" (string)

    m_VCAutomaticAdd 1 (bool)
    m_VCDebugCom 0 (bool)
    m_VCDebugCmd 0 (bool)
    m_VCDebugOut 0 (bool)
    m_SemanticMergeMode 2 (int)
    m_VCShowFailedCheckout 1 (bool)

ScriptableObject

ScriptableObject可以用來儲存專案中的共享資料,可用來儲存大量資料。

影響範圍

ScriptableObject可以建立一個asset,我們可以在Inspector面板中調整資料的值,也可以通過程式碼進行修改並儲存。注意,這是一個asset,所以在遊戲執行期間對ScriptableObject的資料修改後資料不會恢復。

這裡寫圖片描述
using UnityEngine;
using UnityEditor;

[CreateAssetMenu]
public class NewBehaviourScript : ScriptableObject
{
    [Range(0,10)]
    public int number = 3;

    public bool toggle = false;

    public string[] texts = new string[5];
}

儲存什麼

編輯器擴充套件建立的資料和配置檔案,可以當做一個小型資料庫。

儲存位置

可以放在Asset目錄

JSON

如何使用

用法很簡單,使用JsonUtility.ToJson和JsonUtility.FromJson。注意:只對序列化欄位進行json轉換

[Serializable]
public class Example
{
    [SerializeField]
    string name = "hoge";

    [SerializeField]
    int number = 10;
}

/*
次のJSONデータが出力される
{
    "name": "hoge",
    "number": 10
}
*/
Debug.Log(JsonUtility.ToJson(new Example(), true));

JsonUtility和EditorJsonUtility

https://docs.unity3d.com/ScriptReference/JsonUtility.ToJson.html
JsonUtility只支援對MonoBehaviour,ScriptableObject及派生類和普通類進行序列化,而UnityEngine.Object 的子類則不能被其序列化,但我們可以再編輯器中使用EditorJsonUtility來對UnityEngine.Objec的子類進行序列化。

直接序列化陣列不會產生包含每個元素的json,而是一個包含了陣列物件public域的一個物件json。所以要序列化陣列需要把它封裝在一個class或struct裡面。

使用EditorJsonUtility.toJson來序列化UnityEngine.Object子類

/*
次のような JSON を取得できる
{"key名":[{"name":"hoge"},{"name":"hoge"}]}
*/
public static string ToJson(string key, Object[] objs)
{
    var json = objs.Select(obj => EditorJsonUtility.ToJson(obj)).ToArray();
    var values = string.Join(",", json);
    return string.Format("{\"{0}\":{1}]}", key, values);;
}

如果序列化類的欄位變數可序列化

對於列表,我們可以建立自己的列表型別, 可以直接對改型別的列表序列化:

/*
使い方は List とほぼ同じ(AddRange がないので自作する)
*/
[Serializable]
public class SerializableList<T> : Collection<T>, ISerializationCallbackReceiver
{
    [SerializeField]
    List<T> items;

    public void OnBeforeSerialize()
    {
        items = (List<T>)Items;
    }

    public void OnAfterDeserialize()
    {
        Clear();
        foreach (var item in items)
            Add(item);
    }
}

public class Test
{
    [MenuItem("Test/Test")]
    public static void TestMethod()
    {
        var serializedList = new SerializableList<Example>
        {
            new Example(),
            new Example()
        };
        Debug.Log(JsonUtility.ToJson(serializedList));
    }
}

[Serializable]
public class Example
{
    [SerializeField]
    string name = "hoge";

    [SerializeField]
    int number = 10;

    public string[] var = new string[4]{"1", "2", "3", "4"};
}

輸出:

{"items":[{"name":"hoge","number":10,"var":["1","2","3","4"]},{"name":"hoge","number":10,"var":["1","2","3","4"]}]}

我們還可以自定義ToList方法來格式化json

public string ToJson()
{
    var result = "[]"
    var json = JsonUtility.ToJson(this);
    var regex = new Regex("^{\"items\":(?<array>.*)}$");
    var match = regex.Match(json);
    if (match.Success)
        result = match.Groups["array"].Value;

    return result;
}

當然,有了序列化方法,也得有相應的反序列化方法:

public static SerializableList<T> FromJson(string arrayString)
{
    var json = "{\"items\":" + arrayString + "}";
    return JsonUtility.FromJson<SerializableList<T>>(json);
}