Version: 2017.3
Serialización incorporada
Errores de serialización de scripts

Serialización personalizada

La serialización es el proceso automático de transformación de estructuras de datos o estados de objeto en un formato que Unity puede almacenar y reconstruir más tarde. (Consulte la documentación sobre Script Serialization para obtener más información sobre la serialización de Unity).

A veces es posible que desee serializar algo que el serializador de Unity no admite. En muchos casos, el mejor enfoque es utilizar callbacks de serialización. (Consulte la Referencia de la API de scripting de Unity: ISerializationCallbackReceiver para obtener más información sobre la serialización personalizada mediante llamadas de serlización).

Las callbacks de serialización le permiten recibir notificaciones antes de que el serializador lea los datos de sus campos y después de que haya terminado de escribirlos. Puede usar callbacks de serialización para dar a sus datos difíciles de serializar una representación diferente en el tiempo de ejecución a su representación cuando realmente serializa.

Para hacer esto, transforme sus datos en algo que Unity entienda justo antes de que Unity quiera serializarlo. Luego, justo después de que Unity haya escrito los datos en sus campos, puede transformar el formulario serializado de nuevo en la forma en que desea tener sus datos en tiempo de ejecución

Por ejemplo: desea tener una estructura de datos de árbol. Si permite que Unity serialice directamente la estructura de datos, la limitación de “no admitir nulo” provocará que su flujo de datos sea muy grande, lo que provocará degradaciones en el rendimiento en muchos sistemas. Esto se muestra en el Ejemplo 1, a continuación.

Ejemplo 1: Serialización directa de Unity, que conduce a problemas de rendimiento


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

En cambio, le dice a Unity que no serialice el árbol directamente, y crea un campo separado para almacenar el árbol en un formato serializado, adecuado para el serializador de Unity. Esto se muestra en el Ejemplo 2, a continuación.

** Ejemplo 2 **: evitar serialización directa de Unity y evitar problemas de rendimiento



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 Page published with editorial review

Serialización incorporada
Errores de serialización de scripts