Version: 2019.2
빌트인 직렬화
스크립트 직렬화 오류

커스텀 직렬화

직렬화는 데이터 구조 또는 오브젝트 상태를 Unity가 보관하고 나중에 다시 복구할 수 있는 포맷으로 변환하는 자동 프로세스입니다. Unity의 직렬화에 대한 자세한 내용은 스크립트 직렬화를 참조하십시오.

Unity의 시리얼라이저가 지원하지 않는 항목을 직렬화해야 할 경우도 있습니다. 대부분의 경우 가장 좋은 접근 방법은 직렬화 콜백을 사용하는 것입니다. 직렬화 콜백을 사용한 커스텀 직렬화에 대한 자세한 내용은 Unity 스크립팅 API 레퍼런스 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";
        //The field below is what makes the serialization data become huge because
        //it introduces a 'class cycle'.
        public List<Node> children = new List<Node>();
    }
    //this gets serialized
    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 class that is used at runtime.
    // This is internal to the BehaviourWithTree class and is not serialized.
    public class Node {
        public string interestingValue = "value";
        public List<Node> children = new List<Node>();
    }
    // Node class that we will use for serialization.
    [Serializable]
    public struct SerializableNode {
        public string interestingValue;
        public int childCount;
        public int indexOfFirstChild;
    }
    // The root node used for runtime tree representation. Not serialized.
    Node root = new Node();
    // This is the field we give Unity to serialize.
    public List<SerializableNode> serializedNodes;
    public void OnBeforeSerialize() {
        // Unity is about to read the serializedNodes field's contents.
        // The correct data must now be written into that field "just in time".
        if (serializedNodes == null) serializedNodes = new List<SerializableNode>();
        if (root == null) root = new Node ();
        serializedNodes.Clear();
        AddNodeToSerializedNodes(root);
        // Now Unity is free to serialize this field, and we should get back the expected 
        // data when it is deserialized later.
    }
    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 has just written new data into the serializedNodes field.
        //let's populate our actual runtime data with those new values.
        if (serializedNodes.Count > 0) {
            ReadNodeFromSerializedNodes (0, out root);
        } else
        root = new Node ();
    }
    int ReadNodeFromSerializedNodes(int index, out Node node) {
        var serializedNode = serializedNodes [index];
        // Transfer the deserialized data into the internal Node class
        Node newNode = new Node() {
            interestingValue = serializedNode.interestingValue,
            children = new List<Node> ()
        }
        ;
        // The tree needs to be read in depth-first, since that's how we wrote it out.
        for (int i = 0; i != serializedNode.childCount; i++) {
            Node childNode;
            index = ReadNodeFromSerializedNodes (++index, out childNode);
            newNode.children.Add (childNode);
        }
        node = newNode;
        return index;
    }
    // This OnGUI draws out the node tree in the Game View, with buttons to add new nodes as children.
    void OnGUI() {
        if (root != null)
        Display (root);
    }
    void Display(Node node) {
        GUILayout.Label ("Value: ");
        // Allow modification of the node's "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

빌트인 직렬화
스크립트 직렬화 오류