うにてぃブログ

主にUnityとC#に関する記事を書いていきます

【Unity】ScriptableSingleton のデータを永続化する

ScriptableSingleton のデータをEditorを終了しても確保する場合、独自に保存処理を記述する必要がありました。しかし、Unity2020.1 から FilePathAttribute が追加され、これを利用することで永続化することが可能になりました。

※こちらは2020.3.14f1での確認

使い方

FilePathAttribute のコンストラクタは以下のようになっており

public FilePathAttribute(string relativePath, FilePathAttribute.Location location)

これを ScriptableSingleton を継承したクラスに attribute として追加することで保存が可能になる

[FilePath("Sample/Data.dat", FilePathAttribute.Location.ProjectFolder)]
private class Hoge : ScriptableSingleton<Hoge>
{
}

保存する際には Save() を呼び出す必要があり、保存できるデータはシリアライズされているものに限られる

Save は以下のようになっており、true の場合は YAML 形式で保存、falseの場合はバイナリ形式で保存するようです

    protected virtual void Save(bool saveAsText)

保存範囲の設定

FilePathAttribute の第2引数が保存範囲になっており、用途に合わせて以下を設定できる

FilePathAttribute.Location.PreferencesFolder

全部の Unity Project で共有

FilePathAttribute.Location.ProjectFolder

プロジェクト毎に保存

保存先

データの保存先は上記と同じく設定によって異なる

PreferencesFolder の場合は InternalEditorUtility.unityPreferencesFolder 以下
ProjectFolder の場合は Project の Root 以下 に作られる

    private static string CombineFilePath(string relativePath, FilePathAttribute.Location location)
    {
      if (relativePath[0] == '/')
        relativePath = relativePath.Substring(1);
      switch (location)
      {
        case FilePathAttribute.Location.PreferencesFolder:
          return InternalEditorUtility.unityPreferencesFolder + "/" + relativePath;
        case FilePathAttribute.Location.ProjectFolder:
          return relativePath;
        default:
          Debug.LogError((object) ("Unhandled enum: " + location.ToString()));
          return relativePath;
      }
    }

サンプル

using System;
using UnityEditor;
using UnityEngine;
 
[FilePath("Sample/Data.json", FilePathAttribute.Location.ProjectFolder)]
public class SampleScriptableSingleton : ScriptableSingleton<SampleScriptableSingleton>
{
    [MenuItem("Tools/SampleScriptableSingletonSave")]
    private static void Test()
    {
        instance.Value++;
        instance.serializeData.Value++;
        instance.nonSerializeData.Value++;
        // YAML形式で保存
        instance.Save(true);
        Debug.Log($"Int:{instance.Value}\n" +
                  $"Serialize:{instance.serializeData.Value}\n" +
                  $"NonSerialize:{instance.nonSerializeData.Value}");
    }
 
    public int Value;
    [SerializeField]
    private SerializeData serializeData = new SerializeData();
 
    private NonSerializeData nonSerializeData = new NonSerializeData();
     
    /// <summary>
    /// 保存されるデータ
    /// </summary>
    [Serializable]
    private class SerializeData
    {
        public int Value;
    }
     
    /// <summary>
    /// 保存されないデータ
    /// </summary>
    private class NonSerializeData
    {
        public int Value;
    }
}