シリアル化は、データ構造やオブジェクトの状態を、Unity に保存し、後で再構築できる形式に変換する自動プロセスです。(Unity のシリアル化の詳細については、スクリプトのシリアル化 に関するドキュメントを参照してください。)
Unity がサポートしていない形式のデータをシリアライズしたい場合があるかもしれません。多くの場合、最善の方法はシリアル化コールバックを使用することです。(シリアル化コールバックを使用したカスタムシリアライゼーションの詳細については、Unity のスクリプトリファレンス ISerializationCallbackReceiver を参照してください。)
シリアル化コールバックを使用すると、シリアライザーがフィールドからデータを読み取る前とフィールドへの書き込みが完了した後に通知を受けることができます。シリアル化コールバックを使用すると、ランタイムにシリアライズするのが難しいデータを、実際にシリアライズする形式にすることができます。
これを行うには、Unity がデータをシリアライズする直前に、Unity が理解できる形式に変換します。その後、Unity がデータをフィールドに書き込んだ直後に、シリアライズされた形式をランタイムにあるべき形式に戻します。
例えば、ツリーのデータ構造が必要だとします。Unity にデータ構造を直接シリアライズさせると、「null をサポートしない」という制限によってデータストリームが非常に大きくなり、多くのシステムでパフォーマンスが低下します。以下の例1がこのケースです。
例 1: Unity の直接シリアル化。パフォーマンスが低下の原因になります。
using UnityEngine;
using System.Collections.Generic;
using System;
public class VerySlowBehaviourDoNotDoThis : MonoBehaviour {
[Serializable]
public class Node {
public string interestingValue = "value";
//以下のフィールドは、データストリームが非常に大きくなる原因となります。
//なぜなら、'class cycle' を発生させるからです
public List<Node> children = new List<Node>();
}
//これがシリアライズされます
public Node root = new Node();
void OnGUI() {
Display (root);
}
void Display(Node node) {
GUILayout.Label ("Value: ");
node.interestingValue = GUILayout.TextField(node.interestingValue, GUILayout.Width(200));
GUILayout.BeginHorizontal ();
GUILayout.Space (20);
GUILayout.BeginVertical ();
foreach (var child in node.children)
Display (child);
if (GUILayout.Button ("Add child"))
node.children.Add (new Node ());
GUILayout.EndVertical ();
GUILayout.EndHorizontal ();
}
}
改善案として、Unity にツリーを直接シリアライズさせずに、別のフィールドを作り、ツリーを Unity に適したシリアライズした形式で保存します。以下の例 2 がこのケースです。
例 2: Unity の直接シリアル化を避け、パフォーマンスの低下を避けます。
using System.Collections.Generic;
using System;
public class BehaviourWithTree : MonoBehaviour, ISerializationCallbackReceiver {
// ランタイムに使用される Node クラス
// これは BehaviourWithTree クラスの内側であり、シリアライズされません
public class Node {
public string interestingValue = "value";
public List<Node> children = new List<Node>();
}
// シリアル化に使用する Node クラス
[Serializable]
public struct SerializableNode {
public string interestingValue;
public int childCount;
public int indexOfFirstChild;
}
// ランタイムのツリーに使用する root ノード。シリアライズされません。
Node root = new Node();
// Unity にシリアライズさせるために用意したフィールド
public List<SerializableNode> serializedNodes;
public void OnBeforeSerialize() {
// Unity が serializedNodes フィールドのコンテンツの読み取りを始めます
// 間に合うように、正しいデータがフィールドに書き込まれなくてはなりません
if (serializedNodes == null) serializedNodes = new List<SerializableNode>();
if (root == null) root = new Node ();
serializedNodes.Clear();
AddNodeToSerializedNodes(root);
// Unity がフィールドをシリアライズできるようになりました。
// 後に、デシリアライズすると、正しい形式のデータを得られます。
}
void AddNodeToSerializedNodes(Node n) {
var serializedNode = new SerializableNode () {
interestingValue = n.interestingValue,
childCount = n.children.Count,
indexOfFirstChild = serializedNodes.Count+1
}
;
serializedNodes.Add (serializedNode);
foreach (var child in n.children)
AddNodeToSerializedNodes (child);
}
public void OnAfterDeserialize() {
//Unity によって serializedNodes フィールドに新しいデータが書き込まれました。
//実際のランタイムデータにこれらの新しい値を入力しましょう。
if (serializedNodes.Count > 0) {
ReadNodeFromSerializedNodes (0, out root);
} else
root = new Node ();
}
int ReadNodeFromSerializedNodes(int index, out Node node) {
var serializedNode = serializedNodes [index];
// デシリアライズされたデータを内側の Node クラスに送信します
Node newNode = new Node() {
interestingValue = serializedNode.interestingValue,
children = new List<Node> ()
}
;
// ツリーは、そのように作られているため、深度を最初に読み取る必要があります。
for (int i = 0; i != serializedNode.childCount; i++) {
Node childNode;
index = ReadNodeFromSerializedNodes (++index, out childNode);
newNode.children.Add (childNode);
}
node = newNode;
return index;
}
//OnGUI はゲームビューにノードツリーを描画します。ボタンで新しい子のノードを加えることができます。
void OnGUI() {
if (root != null)
Display (root);
}
void Display(Node node) {
GUILayout.Label ("Value: ");
// ノードの "interesting value" の修正が可能です
node.interestingValue = GUILayout.TextField(node.interestingValue, GUILayout.Width(200));
GUILayout.BeginHorizontal ();
GUILayout.Space (20);
GUILayout.BeginVertical ();
foreach (var child in node.children)
Display (child);
if (GUILayout.Button ("Add child"))
node.children.Add (new Node ());
GUILayout.EndVertical ();
GUILayout.EndHorizontal ();
}
}
• 2017–05–15 公開ページ
Did you find this page useful? Please give it a rating:
Thanks for rating this page!
What kind of problem would you like to report?
Thanks for letting us know! This page has been marked for review based on your feedback.
If you have time, you can provide more information to help us fix the problem faster.
Provide more information
You've told us this page needs code samples. If you'd like to help us further, you could provide a code sample, or tell us about what kind of code sample you'd like to see:
You've told us there are code samples on this page which don't work. If you know how to fix it, or have something better we could use instead, please let us know:
You've told us there is information missing from this page. Please tell us more about what's missing:
You've told us there is incorrect information on this page. If you know what we should change to make it correct, please tell us:
You've told us this page has unclear or confusing information. Please tell us more about what you found unclear or confusing, or let us know how we could make it clearer:
You've told us there is a spelling or grammar error on this page. Please tell us what's wrong:
You've told us this page has a problem. Please tell us more about what's wrong:
Thank you for helping to make the Unity documentation better!
Your feedback has been submitted as a ticket for our documentation team to review.
We are not able to reply to every ticket submitted.