Version: 2017.4
ビルトインのシリアル化
スクリプトのシリアル化に関連するエラー

カスタム製のシリアル化

シリアル化は、データ構造やオブジェクトの状態を、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 編集レビュー 無しにパブリッシュされたページ

ビルトインのシリアル化
スクリプトのシリアル化に関連するエラー