また備忘です。長文です。reflection使えばprivateもできるかも。コストも読み書きの時だけで安いし。
今回はUnityで大人気の有償アセットEasySave3についてです。
保存できる型はデフォルトで基本の型のほとんどがサポートされています。
が、ネストしたデータやサポートにない独自の型のセーブロードどうすればいいの…ってときです。
大きく分けて対策2個あると思うんです。一長一短ある。
・ネストした型をJSONにパースしてstringに変換後に保存!
JsonUtility をつかって Unity で JSON を取り扱う方法
・独自のセーブロードタイプクラスを作成(今回はこ↑こ↓)
結論を書くと、自分でES3用のセーブロードタイプクラスを作るのです。
公式のマニュアルに書いてあるのでこちらを参考してください。
・独自クラス作成前セーブデータ(保存できてない)
"LanguageDatabase" : {
"__type" : "Databases.DatabaseObject,Assembly-CSharp",
"value" : {
"__type" : "Databases.LanguageDatabase,Assembly-CSharp",
"_ES3Ref" : "1380498291805822379",
"_ES3Ref" : "1380498291805822379"
}
},
・独自クラス作成後セーブデータ(どやぁ)
"LanguageDatabase" : {
"__type" : "Databases.DatabaseObject,Assembly-CSharp",
"value" : {
"__type" : "Databases.LanguageDatabase,Assembly-CSharp",
"_ES3Ref" : "5303085288931533276",
"_ES3Ref" : "5303085288931533276",
"Items" : [
{
"Language" : "English",
"CompoItems" : [
{
"Type" : 1,
"LanguageData" : [
{
"ID" : 0,
"SearchID" : "KeyName",
"Word" : "Key Name"
},{
"ID" : 1,
"SearchID" : "BindName",
"Word" : "Bind Key"
},{
]
},{
"Type" : 2,
"LanguageData" : [
{
"ID" : 60,
"SearchID" : "TXT_HotKey",
"Word" : "HotKey"
},{
"ID" : 61,
"SearchID" : "TXT_Video",
"Word" : "Video"
},{
},{
]
},{
"Language" : "日本語",
"CompoItems" : [
{
"Type" : 1,
"LanguageData" : [
{
"ID" : 0,
"SearchID" : "KeyName",
"Word" : "名称"
},{
"ID" : 1,
"SearchID" : "BindName",
"Word" : "設定キー"
},{
以下略
※SakuraEditorで開くと文字コードがShiftJisでに設定された文字化けするけど、
UTF-8 Bom付でもう一度開けば文字化けは解消されます。(文字コードは怖いね)
保存対象のデータ構造(Odin使ってます。見にくい人いるかも、とりあえずネストした構造です。)
セーブを実行しているコードと、クラス・構造体もペタっとおいておきます。ソロ開発のコードなので好きに見ていきなペッ!
ES3.Save(GetType().Name, (LanguageDatabase.Instance)this, dataPath); //SaveData.csのinstanceされたデータ
[System.Serializable] // 保存対象
public class LanguageDatabase : SingletonDatabase<LanguageDatabase>, IMasterData
{
// いろんな項目が多々ある
public List<LanguageDataList> Items = new List<LanguageDataList>(); //保存されるデータ
// いろんなメソッドが多々ある
}
[System.Serializable] // Serializeなデータ
public class LanguageDataList
{
public string Language;
public List<LanguageCompoTypeData> CompoItems = new List<LanguageCompoTypeData>();
public LanguageDataList(string val)
{
Language = val;
}
}
[System.Serializable]
public class LanguageCompoTypeData
{
public CompoLangType Type = CompoLangType.None;
public List<LanguageData> LanguageItems = new List<LanguageData>();
public LanguageCompoTypeData(CompoLangType type)
{
Type = type;
}
}
[System.Serializable]
public class LanguageData
{
public int ID;
public string SearchID;
public string Word;
public string GetWord() {
return @Word;
}
}
今回作ったもの(重要)
ネストした個数分タイプを作りました。 ~~#一番下層のタイプはもしかしたらいらないかも(ES3UserType_LanguageData.cs)~~ 独自の型は全部必要なので定義してください。using Databases;
using System.Collections.Generic;
namespace ES3Types
{
[UnityEngine.Scripting.Preserve]
[ES3PropertiesAttribute()]
public class ES3UserType_LanguageDataList : ES3ObjectType
{
public static ES3Type Instance = null;
public ES3UserType_LanguageDataList() : base(typeof(LanguageDataList)){ Instance = this; priority = 1; }
protected override void WriteObject(object obj, ES3Writer writer)
{
var instance = (LanguageDataList)obj;
writer.WriteProperty<string>("Language", instance.Language);
writer.WriteProperty<List<LanguageCompoTypeData>>("CompoItems", instance.CompoItems);
}
protected override void ReadObject<T>(ES3Reader reader, object obj)
{
var instance = (LanguageDataList)obj;
foreach(string propertyName in reader.Properties)
{
switch(propertyName)
{
case "Language":
instance.Language = reader.Read<string>(); break;
case "CompoItems":
instance.CompoItems = reader.Read<List<LanguageCompoTypeData>>(); break;
default:
reader.Skip();
break;
}
}
}
protected override object ReadObject<T>(ES3Reader reader)
{
var instance = new LanguageDataList("Language");
ReadObject<T>(reader, instance);
return instance;
}
}
}
namespace ES3Types
{
[UnityEngine.Scripting.Preserve]
[ES3PropertiesAttribute()]
public class ES3UserType_LanguageCompoTypeData : ES3ObjectType
{
public static ES3Type Instance = null;
public ES3UserType_LanguageCompoTypeData() : base(typeof(LanguageCompoTypeData)){ Instance = this; priority = 1; }
protected override void WriteObject(object obj, ES3Writer writer)
{
var instance = (LanguageCompoTypeData)obj;
writer.WriteProperty<CompoLangType>("Type", instance.Type);
writer.WriteProperty<List<LanguageData>>("LanguageData", instance.LanguageItems);
}
protected override void ReadObject<T>(ES3Reader reader, object obj)
{
var instance = (LanguageCompoTypeData)obj;
foreach(string propertyName in reader.Properties)
{
switch(propertyName)
{
case "Type":
instance.Type = reader.Read<CompoLangType>(); break;
case "LanguageData":
instance.LanguageItems = reader.Read<List<LanguageData>>(); break;
default:
reader.Skip();
break;
}
}
}
protected override object ReadObject<T>(ES3Reader reader)
{
var instance = new LanguageCompoTypeData(CompoLangType.None);
ReadObject<T>(reader, instance);
return instance;
}
}
}
namespace ES3Types
{
[UnityEngine.Scripting.Preserve]
[ES3PropertiesAttribute()]
public class ES3UserType_LanguageData : ES3ObjectType
{
public static ES3Type Instance = null;
public ES3UserType_LanguageData() : base(typeof(LanguageData)){ Instance = this; priority = 1; }
protected override void WriteObject(object obj, ES3Writer writer)
{
var instance = (LanguageData)obj;
writer.WriteProperty<int>("ID", instance.ID);
writer.WriteProperty<string>("SearchID", instance.SearchID);
writer.WriteProperty<string>("Word", instance.Word);
}
protected override void ReadObject<T>(ES3Reader reader, object obj)
{
var instance = (LanguageData)obj;
foreach(string propertyName in reader.Properties)
{
switch(propertyName)
{
case "ID":
instance.ID = reader.Read<int>(); break;
case "SearchID":
instance.SearchID = reader.Read<string>(); break;
case "Word":
instance.Word = reader.Read<string>(); break;
default:
reader.Skip();
break;
}
}
}
protected override object ReadObject<T>(ES3Reader reader)
{
var instance = new LanguageData();
ReadObject<T>(reader, instance);
return instance;
}
}
}
以上です。お疲れ様です。
解説はとくにありません・・・
WriteObjectとReadObjectがそれぞれ対で設定できてればオッケー!
ReadObjectは今回の場合はほとんどいらないかも。一応作ってるけど。
EasySave3のソースコードのぞいてみるとわかるけど、
プリミティブ型⇒Listとか汎用な型⇒Unity謹製型⇒独自型⇒ないならエラー!
ってとこでList構造見つけるまではいいけどListに指定されている型が謎だからできんってなってる。
そこに今回の独自型を作成して、参照を自動的にお願いしているだけです。
https://qiita.com/AllNight/items/cd8d7b6111a9f75afb55
上記の記事で良い感じのデータ定義と、データロードができるようになりました。
公式でぜひ採用してほしいレベル(何)