Мои Уведомления
Привет, !
Мой Аккаунт Мои Финансы Мои Подписки Мои Настройки Выход
Руководство API скрипты

Пользовательская сериализация

Сериализация — это автоматический процесс преобразования структур данных или состояний объектов в формат, который Unity может сохранять и реконструировать позже. (Дополнительную информацию о сериализации Unity см. в документации по Сериализации сценариев.)

Иногда вам может понадобиться сериализовать то, что не поддерживает сериализатор Unity. Во многих случаях лучшим подходом является использование обратных вызовов сериализации. (Дополнительную информацию о пользовательской сериализации с использованием обратных вызовов сериализации см. в Справочнике API сценариев Unity: ISerializationCallbackReceiver.)

Обратные вызовы сериализации позволяют получать уведомления до того, как сериализатор прочитает данные из ваших полей и после того, как он закончит запись в них. Вы можете использовать обратные вызовы сериализации, чтобы предоставить трудно сериализуемым данным представление во время выполнения, отличное от их представления при фактической сериализации.

Для этого преобразуйте свои данные во что-то понятное Unity прямо перед тем, как Unity захочет их сериализовать. Затем, сразу после того, как Unity запишет данные в ваши поля, вы можете преобразовать сериализованную форму обратно в форму, в которой вы хотите хранить данные во время выполнения

Например: вы хотите иметь древовидную структуру данных. Если вы позволите Unity напрямую сериализовать структуру данных, ограничение «нет поддержки нулевого значения» приведет к тому, что ваш поток данных станет очень большим, что приведет к снижению производительности во многих системах. Это показано в примере 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 children = new List(); } //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 children = new List(); } // 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 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(); 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 () } ; // 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 2021.3