Version: Unity 6.0 (6000.0)
언어 : 한국어
직렬화 규칙
Unity가 직렬화를 사용하는 방법

커스텀 직렬화

Unity의 시리얼라이저가 지원하지 않는 요소(C# 딕셔너리 등)를 직렬화하려는 경우, 클래스에 ISerializationCallbackReceiver 인터페이스를 구현할 수 있습니다. 이를 통해 Unity가 직렬화와 역직렬화 시 키 포인트에서 호출하는 콜백을 다음과 같이 구현할 수 있습니다.

직렬화 콜백을 사용하면 직렬화하기 어려운 데이터를 런타임과 직렬화 시점에 서로 다른 형식으로 표현할 수 있습니다. Unity가 데이터를 직렬화하기 직전에 해당 데이터를 Unity가 이해하는 데이터로 변환할 수 있습니다. Unity가 데이터를 필드에 작성한 후에는 직렬화된 데이터를 런타임 시 원하는 형태로 다시 변환할 수 있습니다.

  1. 오브젝트를 직렬할 때 Unity는 OnBeforeSerialize() 콜백을 호출합니다. 이 콜백에서 데이터를 Unity가 이해하는 데이터로 변환할 수 있습니다. 예를 들어 C# 딕셔너리를 직렬화하려면 딕셔너리의 데이터를 키 배열과 값 배열로 복사합니다.
  2. OnBeforeSerialize() 콜백이 종료되면 Unity는 배열을 직렬화합니다.
  3. 나중에 오브젝트가 역직렬화될 때 Unity는 OnAfterDeserialize() 콜백을 호출합니다. 이 콜백에서는 메모리에 있는 오브젝트에 적합한 형태로 데이터를 다시 변환할 수 있습니다. 예를 들어 키와 값 배열을 사용하여 C# Dictionary를 다시 채울 수 있습니다.

예제1: Unity의 기본 직렬화로 인해 성능 문제 발생

트리 데이터 구조를 원한다고 가정해 보겠습니다. Unity가 데이터 구조를 직접 직렬화하도록 하면 ‘null 지원 없음’ 제약으로 인해 데이터 스트림이 매우 비대해져 많은 시스템에서 성능이 저하됩니다.


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 ();
    }
}

예제2: 커스텀 직렬화로 성능 문제 방지

이 경우 Unity가 직접 트리를 직렬화하지 않도록 하고 별도의 필드에 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 ();
    }
}



추가 리소스

직렬화 규칙
Unity가 직렬화를 사용하는 방법
Copyright © 2023 Unity Technologies
优美缔软件(上海)有限公司 版权所有
"Unity"、Unity 徽标及其他 Unity 商标是 Unity Technologies 或其附属机构在美国及其他地区的商标或注册商标。其他名称或品牌是其各自所有者的商标。
公安部备案号:
31010902002961