Unity Editor拡張入門(3)データの保存

20276 ワード

Unity Editor拡張入門(3)データの保存
Translated by Xdestiny 2018/3/4
日本語の原文の住所:http://anchan828.github.io/editor-manual/web/data_storge.
一部のものはかなり不確実です.転載禁止です.
エディタ拡張機能が動作している状態で、後で使うために数値を保存する必要があります.これらの値はエディタ拡張機能に関する設定やゲームに関するパラメータなどかもしれません.ユニティにデータを保存する手段は大きく三つに分けられます.この章では目的に応じて適切な手段を紹介し、解説します.
3.1 EditorPref
PCで共有できるデータを保存します.Unity項目に制限されず、Unityエディタで共有する必要がある場合に適しています.
影響範囲
保存された値はUnityの主要バージョンUnity 4.xの保存値に影響します.Unity 4.xのバージョンでのみ処理されます.ユニティ5.xも同じです.
何を保存しますか
EditorProfsに保存すべきものは、位置、サイズ、ユニティ・エディターの環境設定(Preferencesのようなもの)です.単独のAsetでも環境に関する設定があれば、Editorsを使ってください.注意したいのは、Editorsに保存されているのは全部明文です.パスワードなどの重要な情報は絶対に保存しないでください.
Platform
保存先
Windows(Unity 4.x)
HKEY_CURRENT_USER\Software\Unity Technologies\UnityEditor 4.x
Windows(Unity 5 x)
HKEY_CURRENT_USER\Software\Unity Technologies\UnityEditor 5.x
Mac OS X(Unity 4.x)
~/Library/Preferences/comp.unity 3 d.Unity Editor 4.plist
Mac OS X(Unity 5.x)
~/Library/Preferences/comp.unity 3 d.Unity Editor 5 x.plistEditorPrefは、主要バージョンに従って保存されます.特にWindowsはレジストリに値を保存します.この過程で誤った設定をしたら、最悪の場合はWindowsが起動できなくなる可能性があります.特に注意してください.
使い方OnEnableと同様に、一度以上の関数を呼び出して値を取得することができる.値が変化した時はエディトレットに保存します.
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  ();
    }

    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  ();
    }

    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);
    }
}
3.2 Editor SerSettings.Set/Get ConfigValue
項目でデータの保存方法を共有できます.ここに保存されているデータはいずれも暗号化されており、個人情報系やパスワードなどを保存するのに適しています.
影響範囲と保存場所
このAPIを使って保存したデータは、自分のプロジェクトに影響を与えるだけです.データの保存先はLibrary/EditorUserSettings.assetですので、Libraryフォルダは雨の中で他の人と共有しないと、他の人と情報を共有できなくなります.
何を保存しますか
彼らを登録するためには、メールアドレスやパスワードなどが必要です.Oauthのアクセストークンも同じです.EditorUserSettings.assetはバイナリを使用して保存されているので、容易に見ることは不可能です.ただし、Unityはbinary2textを提供しており、バイナリをテキスト形式に変換することに注意が必要です.
使い方
データを保存してみましょう.
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)
3.3 Script ableObject
Project内でデータを共有する保存方法.いろいろな場合に有効です.ゲーム中の設定、大量のデータを保存するにはこの方法を選択します.
影響範囲ScriptableObjectは、Unityプロジェクトにおいてデータを保存する主な方法である.Unityプロジェクトでは、いつでもデータをAsetとして保存できます.Scriptを通じてデータを読み取ることができます.
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];
}
何を保存しますか
Editor拡張により生成されたAsssetデータや設定ファイル、コンパイル後にゲームデータとして使用され、データベースとして機能します.
Script ableObjectを保存するところ
Projectの下のAsetsフォルダの中にあればいいです.エディタの拡張に特化したScriptableObjectなら、エディターフォルダの下に保存する傾向があります.
使い方
内容が多いので、詳しくは第四章の「ScraptleObject」で紹介しています.
3.4 JSON
JSONはテキスト形式でデータを格納する言語です.一般的にはWebクライアントがサーバからデータを取得する場合、このような形でデータを組織します.しかし、このデータの保存方法は限られておらず、多くの分野で応用されています.Unity 5.3からJsonUtilityというモジュールが正式に追加されました.JSONに対して公式な解決方法があります.しかし、この解決法は一般的に使われているJSONライブラリに比べて、速度や性能に優れておらず、使用にも制限があります.ObjectとJSONとの間の相互変換の条件はUnityの順序付け条件と同じである.以下は具体的な条件です.-クラスにSerializable属性が追加されました.フィールドの上にSerializeField属性があります.または、共有フィールド-その他の条件は第五章「Serialized Object」の詳細を参照してください.
ユニティでは順番に並べられないものについてもJSONは順番に並べられません.順序付けできないDictionaryタイプ-類似のobject[]List object
- (JsonUtility.ToJson(List))

は で、JsonUtility.ToJsonを び してJsonUtility.FromJsonとObjectと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とEditoJsonUtilityJsonUtilityによって、UnityEngine.Objectを JSON UnityEngine.ObjectとJSONとの の に することはできません.EditorJsonUtilityによって われます.ただ、EditorJsonUtilityは の に し がかかります.
/*
         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);;
}

ほとんどのJSONライブラリは の けを することができる.ただし、 じ れをJsonUtilityで してはいけません.
var list = new List{
  new Example(),
  new Example()
};

/*
   {}
              
[{"name":"hoge","number":10},{"name":"hoge","number":10}]
*/
JsonUtility.ToJson(list)
どうしても が なら、 が です.
け なクラスのフィールドは、 けされます.
クラスのフィールドは、 されますので、まずこの を します. は、 Listをプロビジョニングするコードである.
/*
     List  (  AddRange,      )
*/
[Serializable]
public class SerializableList : Collection, ISerializationCallbackReceiver
{
    [SerializeField]
    List items;

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

    public void OnAfterDeserialize()
    {
        Clear();
        foreach (var item in items)
            Add(item);
    }
}
このときJsonUtilityを いてプログレッシブ すると、 のような が られる.
var serializedList = new SerializableList
{
    new Example(),
    new Example()
};

/*
        JSON
{"items":[{"name":"hoge","number":10},{"name":"hoge","number":10}]}
*/
Debug.Log(JsonUtility.ToJson(serializedList));
ここのポイントはISerializationCallbackReceiverです.JsonUtilityにより、ObjectをJSONに すると、ISerializationCallbackReceiverOnBeforeSerializeおよびOnAfterSerializeが び される.この を して、ToJson を び した 、Objectは、プログレッシブ なフィールドに され、 な を する. ができるのはいいことですが、 のJSONの は を った がいいです.(つまり、itemsというキー がない)SerializableListクラスのToJsonメソッドを し、コンテンツの を する を する.
public string ToJson()
{
    var result = "[]"
    var json = JsonUtility.ToJson(this);
    var regex = new Regex("^{\"items\":(?.*)}$");
    var match = regex.Match(json);
    if (match.Success)
        result = match.Groups["array"].Value;

    return result;
}
このToJsonメソッドを び すと、 の が られます.
var serializedList = new SerializableList
{
    new Example(),
    new Example()
};

/*
          
[{"name":"hoge","number":10},{"name":"hoge","number":10}]
*/
Debug.Log(serializedList.ToJson());
しかし、この な は、アンチプロローグをサポートしていないので、 でFromJson を する があります.
public static SerializableList FromJson(string arrayString)
{
    var json = "{\"items\":" + arrayString + "}";
    return JsonUtility.FromJson>(json);
}
これで プロローグを することができます.
var serializedList = new SerializableList<Example>
{
    new Example(),
    new Example()
};

var json = serializedList.ToJson();
var serializableList = SerializableList<Example>.FromJson(json);
//       Example  
Debug.Log(serializableList.Count == 2);
Serializable.csの な は の りです.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text.RegularExpressions;
using UnityEngine;

[Serializable]
public class SerializableList : Collection, ISerializationCallbackReceiver
{
    [SerializeField]
    List items;

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

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

    public string ToJson(bool prettyPrint = false)
    {
        var result = "[]";
        var json = JsonUtility.ToJson(this, prettyPrint);
        var pattern = prettyPrint ? "^\\{
\\s+\"items\":\\s(?.*)
\\s+\\]
}$"
: "^{\"items\":(?.*)}$"; var regex = new Regex(pattern, RegexOptions.Singleline); var match = regex.Match(json); if (match.Success) { result = match.Groups["array"].Value; if (prettyPrint) result += "
]"
; } return result; } public static SerializableList FromJson(string arrayString) { var json = "{\"items\":" + arrayString + "}"; return JsonUtility.FromJson>(json); } }
Dictionaryの JsonUtilityによってDictionaryを することは に である.まず、 のJSONライブラリのようなプログレッシブ はユニティには されないので、ほとんどの は で しなければなりません.これでJsonUtilityを う がなくなります.したがって、MiniJSONのような のユニティに するJSONライブラリを ったほうがいいです.