【Unity】ScriptableSingletonでエディタの状態を保存する

投稿者: | 2023-05-12

ScriptableSingletonを使って、スクリプトの再読み込みやプロジェクトの再起動してもエディタの状態が保存されるようにしてみました。

まずUnityマニュアルの以下のコードを使ってみます。https://docs.unity3d.com/ja/2021.2/ScriptReference/ScriptableSingleton_1.html

ScriptableSingletonを使う

マニュアルのサンプルコードのスクリプトを作ると、メインメニューからスクリプトのModifyとLogメソッドを実行できるようになります。

Logをクリックすると、float型の変数の値が42であることがConsoleに表示されます。この変数はScriptableSingletonクラスを継承したクラスに定義されています。

Modifyをクリックすると保存されて、もう一度Logをクリックすると値が2倍になっていることがわかります。string型のリストにも値が追加されています。

プロジェクトを再起動してもう一度Logを押すと、変更後の値が表示されました。

クラスにFilePath属性をつけると、データが保存されて再起動しても変数の値がリセットされません。

// 「ScriptableSingleton - Unity スクリプトリファレンス」より引用
// https://docs.unity3d.com/ja/2021.2/ScriptReference/ScriptableSingleton_1.html

[FilePath("SomeSubFolder/StateFile.foo", FilePathAttribute.Location.PreferencesFolder)]
public class MySingleton : ScriptableSingleton

属性の引数でファイルの保存場所やファイル名を指定します。第一引数の相対パスが第二引数で選択するルートフォルダと組み合わされます。

第二引数にFilePathAttribute.Location.PreferencesFolderを渡すと、環境設定フォルダが使われます。Windowsの場合は「C:/Users/[username]/AppData/Roaming/Unity/Editor-5.x/Preferences/」です。

FilePathAttribute.Location.ProjectFolderの場合は、AssetsフォルダやLibraryフォルダのある場所と相対パスが結合されます。

ファイルパスはScriptableSingleton<T0>.GetFilePathメソッドで取得できます。ファイルはYAML形式です。

ログを表示するときには、JsonUtility.ToJsonメソッドを使って、publicフィールドのJSON形式のテキストデータを生成しています。

プロパティの場合は表示されませんでした。

これでプロジェクトを開いたときに、保存されたbool値によって自動で開くエディタウィンドウを作ってみました。

ScriptableSingletonの派生クラス

まず、ScriptableSingleton<T0>の派生クラスを作ります。

using UnityEditor;
using UnityEngine;

[FilePath("SubFolder/File.foo", FilePathAttribute.Location.ProjectFolder)]
public class TestSingleton : ScriptableSingleton<TestSingleton>
{
    [SerializeField] bool getWindowOnLoad;
    public bool GetWindowOnLoad { get => getWindowOnLoad; }

    public void Modify(bool getWindowOnLoad)
    {
        this.getWindowOnLoad = getWindowOnLoad;

        Save(true);
        Debug.Log("Saved to: " + GetFilePath());
    }
}

bool値のフィールドとゲッタープロパティ、値をセットして保存するメソッドがあります。

エディタウィンドウ

次に値を変更するトグルや保存ボタンを持つエディタウィンドウを作ります。

using UnityEngine;
using UnityEditor;

public class ScriptableSingletonTest : EditorWindow
{
    [MenuItem("Window/Test/ScriptableSingletonTest")]
    static void CreateWindow()
    {
        EditorWindow.GetWindow<ScriptableSingletonTest>();
    }

    bool getWindowOnLoad;

    void OnEnable()
    {
        // ScriptableSingletonから値を取得
        getWindowOnLoad = TestSingleton.instance.GetWindowOnLoad;
    }

    private void OnGUI()
    {
        // トグルを表示
        getWindowOnLoad = EditorGUILayout.Toggle(getWindowOnLoad);

        // ボタンを押すと
        if (GUILayout.Button("保存"))
        {
            // 値を保存
            TestSingleton.instance.Modify(getWindowOnLoad);
        }
    }

    // エディタを読み込んだときに実行
    [InitializeOnLoadMethod]
    static void OnProjectLoadedInEditor()
    {
        if (!TestSingleton.instance.GetWindowOnLoad) return;

        // アップデートのためのデリゲートに追加
        EditorApplication.update += OnEditorUpdate;
    }

    // コールバック関数
    static void OnEditorUpdate()
    {
        // デリゲートの解除
        EditorApplication.update -= OnEditorUpdate;

        // エディタウィンドウを作成
        CreateWindow();
    }
}

エディタウィンドウを取得する静的メソッドにMenuItem属性を付けてメインメニューから実行できるようにしています。

using UnityEngine;
using UnityEditor;

public class ScriptableSingletonTest : EditorWindow
{
    [MenuItem("Window/Test/ScriptableSingletonTest")]
    static void CreateWindow()
    {
        EditorWindow.GetWindow<ScriptableSingletonTest>();
    }

オブジェクトが有効になったときに、ScriptableSingletonの派生クラスからbool値を取得します。インスタンスは、ScriptableSingleton<T0>.instanceに格納されています。

    bool getWindowOnLoad;

    void OnEnable()
    {
        // ScriptableSingletonから値を取得
        getWindowOnLoad = TestSingleton.instance.GetWindowOnLoad;
    }

エディタウィンドウの内容はOnGUIメソッドに実装します。トグルでbool値を変更して、保存ボタンを押すとシングルトンの値をセットして保存するメソッドを呼びます。

    private void OnGUI()
    {
        // トグルを表示
        getWindowOnLoad = EditorGUILayout.Toggle(getWindowOnLoad);

        // ボタンを押すと
        if (GUILayout.Button("保存"))
        {
            // 値を保存
            TestSingleton.instance.Modify(getWindowOnLoad);
        }
    }

プロジェクトが開かれたときに自動的にエディタウィンドウを開くには、エディタウィンドウを作成するメソッドを、更新処理のためのデリゲートに登録します。

    [InitializeOnLoadMethod]
    static void OnProjectLoadedInEditor()
    {
        if (!TestSingleton.instance.GetWindowOnLoad) return;

        // アップデートのためのデリゲートに追加
        EditorApplication.update += OnEditorUpdate;
    }

OnProjectLoadedInEditorメソッドにはInitializeOnLoadMethod属性がついているので、Unityエディタを読み込んだときに実行されます。保存されたbool値がfalseだとデリゲートに登録しません。

コールバック関数では、デリゲートを解除してエディタウィンドウを作成します。

    static void OnEditorUpdate()
    {
        // デリゲートの解除
        EditorApplication.update -= OnEditorUpdate;

        // エディタウィンドウを作成
        CreateWindow();
    }
}

エディタウィンドウを開き、トグルをオンにして保存ボタンを押します。

エディタウィンドウを閉じてからプロジェクトを再起動すると、エディタウィンドウが自動で開きました。トグルがオフだと開きません。

これで、プロジェクトを再起動しても状態が保存されるようになりました。

参考:https://docs.unity3d.com/ja/2021.2/ScriptReference/ScriptableSingleton_1.html
https://docs.unity3d.com/ja/2021.2/ScriptReference/FilePathAttribute-ctor.html
https://answers.unity.com/questions/1418351/how-to-run-a-script-once-when-a-project-is-first-l.html

コメントを残す

メールアドレスが公開されることはありません。