シリアル化は、データ構造やオブジェクトの状態を、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;
}
// ランタイムのツリー表現に使用されるルートノード。 シリアル化されていません。
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.