Add: serialization section

This commit is contained in:
AnnulusGames 2024-02-19 15:20:40 +09:00
parent 1ec10ab59d
commit 8be6597de0
7 changed files with 183 additions and 0 deletions

View File

@ -0,0 +1,80 @@
# Alchemyのシリアル化プロセス
Alchemyでは、対象の型に`[AlchemySerialize]`属性を付加することで専用のSource Generatorが`ISerializationCallbackReceiver`を自動で実装します。この処理の中で`[AlchemySerializeField]`属性が付加されたフィールドを全て取得し、Unity.Serializationパッケージを用いてJson形式に変換します。ただし、UnityEngine.Objectのフィールドに関してはJson形式で扱うことができないため、単一のListに実体を保存しJsonにはそのindexを書き込みます。
例えば、以下のようなクラスがあったとします。
```cs
using System;
using System.Collections.Generic;
using UnityEngine;
using Alchemy.Serialization;
[AlchemySerialize]
public partial class AlchemySerializationExample : MonoBehaviour
{
[AlchemySerializeField, NonSerialized]
public Dictionary<string, GameObject> dictionary = new();
}
```
これに対し、Alchemyは以下のようなコードを生成します。
```cs
partial class AlchemySerializationExample : global::UnityEngine.ISerializationCallbackReceiver
{
[global::System.Serializable]
sealed class AlchemySerializationData
{
[global::System.Serializable]
public sealed class Item
{
[global::UnityEngine.HideInInspector] public bool isCreated;
[global::UnityEngine.TextArea] public string data;
}
public Item dictionary = new();
[global::UnityEngine.SerializeField] private global::System.Collections.Generic.List<UnityEngine.Object> unityObjectReferences = new();
public global::System.Collections.Generic.IList<UnityEngine.Object> UnityObjectReferences => unityObjectReferences;
}
[global::UnityEngine.HideInInspector, global::UnityEngine.SerializeField] private AlchemySerializationData alchemySerializationData = new();
void global::UnityEngine.ISerializationCallbackReceiver.OnBeforeSerialize()
{
if (this is global::Alchemy.Serialization.IAlchemySerializationCallbackReceiver receiver) receiver.OnBeforeSerialize();
alchemySerializationData.UnityObjectReferences.Clear();
try
{
alchemySerializationData.dictionary.data = global::Alchemy.Serialization.Internal.SerializationHelper.ToJson(this.dictionary , alchemySerializationData.UnityObjectReferences);
alchemySerializationData.dictionary.isCreated = true;
}
catch (global::System.Exception ex)
{
global::UnityEngine.Debug.LogException(ex);
}
}
void global::UnityEngine.ISerializationCallbackReceiver.OnAfterDeserialize()
{
try
{
if (alchemySerializationData.dictionary.isCreated)
{
this.dictionary = global::Alchemy.Serialization.Internal.SerializationHelper.FromJson<System.Collections.Generic.Dictionary<string, UnityEngine.GameObject>>(alchemySerializationData.dictionary.data, alchemySerializationData.UnityObjectReferences);
}
}
catch (global::System.Exception ex)
{
global::UnityEngine.Debug.LogException(ex);
}
if (this is global::Alchemy.Serialization.IAlchemySerializationCallbackReceiver receiver) receiver.OnAfterDeserialize();
}
}
```
このような手法を取っているため`[AlchemySerializeField]`を使用するとシリアライズ/デシリアライズにかかる処理負荷が増加します。そのため可能な限り`[AlchemySerializeField]`の使用を避けることが推奨されます。

View File

@ -0,0 +1,21 @@
# シリアル化データのデバッグ
`[AlchemySerialize]`と同時に`[ShowAlchemySerializationData]`属性を付加することで、Inspector上からシリアル化されたデータを確認することができるようになります。
```cs
using System;
using System.Collections.Generic;
using UnityEngine;
using Alchemy.Serialization;
[AlchemySerialize]
[ShowAlchemySerializationData]
public partial class AlchemySerializationExample : MonoBehaviour
{
[AlchemySerializeField, NonSerialized]
public Dictionary<string, GameObject> dictionary = new();
}
```
![img](../../images/img-show-serialization-data.png)

View File

@ -0,0 +1,21 @@
# シリアル化コールバック
`[AlchemySerialize]`属性を使用するとSource Generatorが`ISerializationCallbackReceiver`を実装するため、通常通り`ISerializationCallbackReceiver`を使用してコールバックを追加することができません。
そのため、Alchemyでは代替となるインターフェースとして`IAlchemySerializationCallbackReceiver`を提供しています。`[AlchemySerialize]`を使用する際には`ISerializationCallbackReceiver`の代わりにこちらを利用してください。
```cs
[AlchemySerialize]
public partial class AlchemySerializationSample : MonoBehaviour, IAlchemySerializationCallbackReceiver
{
public void OnAfterDeserialize()
{
Debug.Log("OnAfterDeserialize");
}
public void OnBeforeSerialize()
{
Debug.Log("OnBeforeSerialize");
}
}
```

View File

@ -0,0 +1,51 @@
# シリアル化拡張
Dictionaryなどの通常のUnityがシリアル化できない型を編集したい場合、`[AlchemySerialize]`属性を使用してシリアル化を行うことができます。
シリアル化拡張を使用したい場合、[Unity.Serialization](https://docs.unity3d.com/Packages/com.unity.serialization@3.1/manual/index.html)パッケージが必要になります。また、リフレクションを用いたUnity.Serializationのシリアル化はUnity2022.1以前のAOT環境で動作しない可能性があります。詳細はパッケージのマニュアルを確認してください。
以下はAlchemyのシリアル化拡張を用いて様々な型をシリアル化し、Inspectorで編集可能にするサンプルです。
```cs
using System;
using System.Collections.Generic;
using UnityEngine;
using Alchemy.Serialization; // Alchemy.Serialization名前空間をusingに追加
// [AlchemySerialize]属性を付加することでAlchemyのシリアル化拡張が有効化されます。
// 任意の基底クラスを持つ型に使用できますが、SourceGeneratorがコード生成を行うため対象型はpartialである必要があります。
[AlchemySerialize]
public partial class AlchemySerializationExample : MonoBehaviour
{
// 対象のフィールドに[AlchemySerializeField]属性と[NonSerialized]属性を付加します。
[AlchemySerializeField, NonSerialized]
public HashSet<GameObject> hashSet = new();
[AlchemySerializeField, NonSerialized]
public Dictionary<string, GameObject> dictionary = new();
[AlchemySerializeField, NonSerialized]
public (int, int) tuple;
[AlchemySerializeField, NonSerialized]
public Vector3? nullable = null;
}
```
![img](../../images/img-serialization-sample.png)
現在Inspectorで編集可能な型は以下の通りです。
* プリミティブ型
* UnityEngine.Object
* AnimationCurve
* Gradient
* 配列
* List<>
* HashSet<>
* Dictionary<,>
* ValueTuple<>
* Nullable<>
* 以上の型のフィールドで構成されるclass/struct
シリアル化の技術的な詳細については[Alchemyのシリアル化プロセス](alchemy-serialization-process.md)を参照してください。

View File

@ -83,6 +83,16 @@
- name: Hierarchyの装飾
href: hierarchy-decoration.md
- name: Serialization
- name: シリアル化拡張
href: serialization-extension.md
- name: Alchemyのシリアル化プロセス
href: alchemy-serialization-process.md
- name: シリアル化データのデバッグ
href: debugging-serialized-data.md
- name: シリアル化コールバック
href: serialization-callback.md
- name: Others
- name: 他ライブラリとの比較
href: comparison-with-other-libraries.md

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB