ほめてください。(解決に2日かかりました。)
前回の記事からEasySave3で自動的にフィールド情報を取得して設定できないか?
といったところからヘルパー作って、ちょっとReader書き換えたらできました。
<前提>
・独自クラスを読み書きする場合には自前でクラス定義をしないといけません。
#たくさんメンバーがあると地味に面倒な作業(文字で定義するから変更があった場合、改修がでかい。)
<手順>
・ES3Readerに3行変更を加えます。
・ヘルパーを適当なクラス名でどこでもいいので配置してください。
・使い方
<注意点>
・データ取得したときにenumの場合は数値ではなく、enumの名前が取得されます。
enum Days{ wed=0,sun=1}の場合、wedやsunです。dynamicのせい。
・今回のデータ作成で作られたデータ以外でやるとエラーが発生することが確認されています。
#writer.Write<>とかwriter.Write<>とか
きちっと動かす場合には、今回のヘルパーでセーブされたデータをロードしてください。
・クラスの変数名でロードフィールドやセーブフィールドを設定しています。
変数名に変更があった場合には既存のデータをきちんと消さないといけません。
#消さなくてもいいです。データが消えるだけなので。
//パス=\Easy Save 3\Scripts\Readers\ES3Reader.cs
~~ 省略 ~~
public virtual void Skip()
{
ReadElement(true);
}
// 追加-start
public virtual T Read<T>(Type enterType)
{
return Read<T>(ES3TypeMgr.GetOrCreateES3Type(enterType));
}
// 追加-end
/// <summary>Reads a value of type T from the reader.</summary>
public virtual T Read<T>()
{
return Read<T>(ES3TypeMgr.GetOrCreateES3Type(typeof(T)));
}
~~ 省略 ~~
using System.Reflection;
namespace ES3Types
{
public static class Helper
{
public static void WriteObject<T>(T data, ES3Writer writer)
{
var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
foreach (var field in fields)
{
if (field.IsNotSerialized) continue;
writer.WriteProperty(field.Name, field.GetValue(data));
}
}
public static void ReadObject<T>(ES3Reader reader, T data)
{
var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
foreach (string propertyName in reader.Properties)
{
bool flag = false;
foreach (var field in fields)
{
if (field.IsNotSerialized) continue;
try
{
if (propertyName == field.Name)
{
dynamic value = reader.Read<dynamic>(field.FieldType);
field.SetValue(data, value);
flag = true; break;
}
}
catch (System.Exception ex)
{
Debug.LogError($"error occurde Type={typeof(T).Name} FieldName={field.Name} FieldType={field.FieldType} ExceptionMsg={ex.Message}");
break;
}
}
if (!flag) reader.Skip();
}
}
}
}
使い方です。
やっぱり独自クラスの定義はしなきゃいけないです。
using Databases;
namespace ES3Types
{
[UnityEngine.Scripting.Preserve]
[ES3PropertiesAttribute()]
public class ES3UserType_UnitStatus : ES3ObjectType
{
public static ES3Type Instance = null;
public ES3UserType_UnitStatus() : base(typeof(UnitStatus)){ Instance = this; priority = 1; }
protected override void WriteObject(object obj, ES3Writer writer)
{
Helper.WriteObject((UnitStatus)obj, writer);
}
protected override void ReadObject<T>(ES3Reader reader, object obj)
{
Helper.ReadObject(reader, (UnitStatus)obj);
}
protected override object ReadObject<T>(ES3Reader reader)
{
var instance = new UnitStatus();
ReadObject<T>(reader, instance);
return instance;
}
}
}
Helperでやってることは簡単でリフレクションって独自クラスの定義を取得するだけです。
内部処理は以下の通り。
1.Public、privateのクラスのフィールドを取得(Serialize関係なく)
2.ただし[System.NonSerialize]がついているフィールドは除外。
リフレクションを使っているのでフィールド以外のデータも取得して設定することが可能です。
ハッピーEasySaveライフをお過ごしください。