unity system serializable что это

Script Serialization

How you organize data in your Unity Project affects how Unity serializes that data and can have a significant impact on the performance of your Project. This page outlines serialization in Unity and how to optimize your Project for it.

Understanding hot reloading

Hot reloading is the process of creating or editing scripts A piece of code that allows you to create your own Components, trigger game events, modify Component properties over time and respond to user input in any way you like. More info
See in Glossary while the Editor is open and applying the script behaviors immediately. You do not have to restart your application or the Editor for changes to take effect.

When you change and save a script, Unity hot reloads all the currently loaded script data. It first stores all serializable variables in all loaded scripts and, after loading the scripts, it restores them. All data that is not serializable is lost after a hot reload.

Saving and loading

Many of the features in the Unity Editor build on top of the core serialization system. Two things to be particularly aware of with serialization are the Inspector window, and hot reloading.

The Inspector window

When you view or change the value of a GameObject The fundamental object in Unity scenes, which can represent characters, props, scenery, cameras, waypoints, and more. A GameObject’s functionality is defined by the Components attached to it. More info
See in Glossary ’s component field in the Inspector window, Unity serializes this data and then displays it in the Inspector window. The Inspector window does not communicate with the Unity Scripting API when it displays the values of a field.

If you use properties in your script, any of the property getters and setters are never called when you view or change values in the Inspector windows as Unity serializes the Inspector window fields directly. This means that: While the values of a field in the Inspector window represent script properties, changes to values in the Inspector window do not call any property getters and setters in your script

Serialization rules

Serializers in Unity run in a real-time game environment. This has a significant impact on performance. As such, serialization in Unity behaves differently to serialization in other programming environments. The following section outlines how to use serialization in Unity. To use field serialization you must ensure it:

Simple field types that can be serialized

Container field types that can be serialized

Note: Unity does not support serialization of multilevel types (multidimensional arrays, jagged arrays, and nested container types). If you want to serialize these, you have two options: wrap the nested type in a class or struct, or use serialization callbacks ISerializationCallbackReceiver to perform custom serialization. For more information, see documentation on Custom Serialization.

How to ensure a custom class can be serialized

To ensure the fields of a custom class or struct are serialized, see How to ensure a field in a script is serialized, above.

When might the serializer behave unexpectedly?

Custom classes behave like structs

With custom classes that are not derived from UnityEngine.Object Unity serializes them inline by value, similar to the way it serializes structs. If you store a reference to an instance of a custom class in several different fields, they become separate objects when serialized. Then, when Unity deserializes the fields, they contain different distinct objects with identical data.

When you need to serialize a complex object graph with references, do not let Unity automatically serialize the objects. Instead, use ISerializationCallbackReceiver to serialize them manually. This prevents Unity from creating multiple objects from object references. For more information, see documentation on ISerializationCallbackReceiver.

No support for null for custom classes

Consider how many allocations are made when deserializing a MonoBehaviour that uses the following script.

It wouldn’t be strange to expect one allocation: That of the Test object. It also wouldn’t be strange to expect two allocations: One for the Test object and one for a Trouble object.

However, Unity actually makes more than a thousand allocations. The serializer does not support null. If it serializes an object, and a field is null, Unity instantiates a new object of that type, and serializes that. Obviously this could lead to infinite cycles, so there is a depth limit of seven levels. At that point Unity stops serializing fields that have types of custom classes, structs, lists, or arrays.

Since so many of Unity’s subsystems build on top of the serialization system, this unexpectedly large serialization stream for the Test MonoBehaviour causes all these subsystems to perform more slowly than necessary.

No support for polymorphism

One way to deal with this limitation is to realize that it only applies to custom classes, which get serialized inline. References to other UnityEngine.Objects get serialized as actual references, and for those, polymorphism does actually work. You would make a ScriptableObject derived class or another MonoBehaviour derived class, and reference that. The downside of this is that you need to store that Monobehaviour or scriptable object somewhere, and that you cannot serialize it inline efficiently.

The reason for these limitations is that one of the core foundations of the serialization system is that the layout of the datastream for an object is known ahead of time; it depends on the types of the fields of the class, rather than what happens to be stored inside the fields.

Optimal use of serialization

You can organise your data to ensure you get optimal use from Unity’s serialization.

Organise your data with the aim to have Unity serialize the smallest possible set of data. The primary purpose of this is not to save space on your computer’s hard drive, but to make sure that you can maintain backwards compatibility with previous versions of the project. Backwards compatibility can become more difficult later on in development if you work with large sets of serialized data.

Organise your data to never have Unity serialize duplicate data or cached data. This causes significant problems for backwards compatibility: It carries a high risk of error because it is too easy for data to get out of sync.

Avoid nested, recursive structures where you reference other classes.The layout of a serialized structure always needs to be the same; independent of the data and only dependent on what is exposed in the script. The only way to reference other objects is through classes derived from UnityEngine.Object. These classes are completely separate; they only reference each other and they don’t embed the contents.

Making Editor code hot reloadable

When reloading scripts, Unity serializes and stores all variables in all loaded scripts. After reloading the scripts, Unity then restores them to their original, pre-serialization values.

Unity never restores static variables, so do not use static variables for states that you need to keep after reloading a script.

Источник

Script Serialization

Serialization of “things” is at the very core of Unity. Many of our features build ontop of the serialization system:

Inspector window. The inspector window doesn’t talk to the C# api to figure out what the values of the properties of whatever it is inspecting is. It asks the object to serialize itself, and then displays the serialized data.

Prefabs. Internally, a prefab is the serialized data stream of one (or more) game objects and components. A prefab instance is a list of modifications that should be made on the serialized data for this instance. The concept prefab actually only exists at editor time. The prefab modifications get baked into a normal serialization stream when Unity makes a build, and when that gets instantiated, the instantiated gameobjects have no idea they were a prefab when they lived in the editor.

Loading. Might not seem surprising, but backwards compatible loading is a system that is built on top of serialization as well. In-editor yaml loading uses the serialization system, but also the runtime loading of scenes, assets and assetbundles uses the serialization system.

Now you’d say that none of this very much concerns you, you’re just happy that it works and want to get on with actually creating some content.

Where it will concern you is that we use this same serializer to serialize MonoBehaviour components, which are backed by your scripts. Because of the very high performance requirements that the serializer has, it does not in all cases behave exactly like what a c# developer would expect from a serializer. In this part of the docs we’ll describe how the serializer works, and some best practices on how to make best use of it.

What does a field of my script need to be in order to be serialized?

Which fieldtypes can we serialize?

What are these situations where the serializer behaves differently from what I expect?

Custom classes behave like structs

If you populate the animals array with three references to a single Animal object, in the serialization stream, you will find 3 objects. when it’s deserialized, there are now three different objects. If you need to serialize a complex object graph with references, you cannot rely on Unity’s serializer doing that all automagically for you, and have to do some work to get that object graph serialized yourself. See the example below on how to serialize things Unity doesn’t serialize by itself.

No support for null for custom classes

Pop quiz. How many allocations are made when deserializing a MonoBehaviour that uses this script:

It wouldn’t be strange to expect 1 allocation. That of the Test object. It also wouldn’t be strange to expect 2 allocations. One for the Test Object, one for a Trouble object. The correct answer is 729. The serializer does not support null. If it serializes an object, and a field is null, we just instantiate a new object of that type, and serialize that. Obviously this could lead to infinite cycles, so we have a relatively magical depth limit of 7 levels. At that point we just stop serializing fields that have types of custom classes/structs and lists and arrays.

Since so many of our subsystems build on top of the serialization system, this unexpectedly large serialization stream for the Test monobehaviour will cause all these subsystems to perform more slowly than necessary. When we investigate performance problems in customer projects, almost always do we find this problem. We added a warning for this situation in Unity 4.5.

No support for polymorphism

if you have a public Animal[] animals and you put in an instance of a dog, a cat and a giraffe, after serialization, you will have three instances of Animal.

One way to deal with this limitation is to realize that it only applies to “custom classes”, which get serialized inline. References to other UnityEngine.Objects get serialized as actual references, and for those polymorphism does actually work. You would make a ScriptableObject derived class or another MonoBehaviour derived class, and reference that. The downside of that is that you need to store that monobehaviour or scriptable object somewhere, and cannot serialize it inline nicely.

The reason for these limitations is that one of the core foundations of the serialization system is that the layout of the datastream for an object is known ahead of time, and depends on the types of the fields of the class, instead of what happens to be stored inside the fields.

I want to serialize something that Unity’s serializer doesn’t support. What do I do?

In many cases the best approach is to use serialization callbacks. They allow you to be notified before the serializer reads data from your fields and after it is done writing to them. You can use this to have a different representation of your hard-to-serialize data at runtime than when you actually serialize. You would use these to transform your data into something Unity understands right before unity wants to serialize it, and you use it to transform the serialized form back into the form you like to have your data in at runtime right after unity has written the data to your fields.

Suppose you want to have a tree datastructure. If you let Unity directly serialize the data structure, the “no support for null” limitation would cause your datastream to become very big, leading to performance degratations in many systems:

Instead, you tell Unity not to serialize the tree directly, and you make a seperate field to store the tree in a serialized format, suited for unity’s serializer:

Beware that the serializer, including these callbacks coming from the serializer usually happen not on the main thread, so you are very limited in what you can do in terms of invoking Unity API. You can however to the necessary data transformations do get your data from a non-unity-serializer-friendly format to a unity-serializer-friendly-format.

Script serialization errors

When scripts call the Unity API from constructors or field initializers, or during deserialization (loading), they can trigger errors. This section demonstrates the poor practise that causes these errors.

In Unity 5.4 these errors do not, in most cases, throw a managed exception, and do not interrupt the execution flow of your scripts. This eases the process of upgrading your project to Unity 5.4. However, these errors will throw a managed exception in subsequent releases of Unity. You should therefore fix any errors as soon as possible when upgrading to 5.4.

Calling Unity API from constructor or field initializers

When Unity creates an instance of a MonoBehaviour or ScriptableObject derived class, it calls the default constructor to create the managed object. This happens before entering the main loop, and before the scene has been fully loaded. Field initializers are also called from the default constructor of a managed object. In general, do not call the Unity API from a constructor, as this is unsafe for the majority of the Unity API.

Examples of poor practice:

Both these cases generate the error message: “Find is not allowed to be called from a MonoBehaviour constructor (or instance field initializer), call in in Awake or Start instead.”

Calling Unity API during deserialization

When Unity loads a scene, it recreates the managed objects from the saved scene and populates them with the saved values (deserializing). In order to create the managed objects, call the default constructor for the objects. If a field referencing an object is saved (serialized) and the object default constructor calls the Unity API, you will get an error when loading the scene. As with the previous error, it is not yet in the main loop and the scene is not fully loaded. This is considered unsafe for the majority of the Unity API.

Example of poor practice:

This generates the error: “Find is not allowed to be called during serialization, call it from Awake or Start instead.”

Источник

Unity system serializable что это

In the spirit of sharing more of the tech behind the scenes, and reasons why some things are the way they are, this post contains an overview of Unity’s serialization system. Understanding this system very well can have a big impact on the effectiveness of your development, and the performance of the things you make. Here we go.

Serialization of “things” is at the very core of Unity. Many of our features build ontop of the serialization system:

The serialization system is written in C++, we use it for all our internal object types (Textures, AnimationClip, Camera, etc). Serialization happens at the UnityEngine.Object level, each UnityEngine.Object is always serialized as a whole. They can contain references to other UnityEngine.Objects and those references get serialized properly.

Now you may say that none of this concerns you very much, you’re just happy that it works and want to get on with actually creating some content. However, this will concern you, as we use this same serializer to serialize MonoBehaviour components, which are backed by your scripts. Because of the very high performance requirements that the serializer has, it does not in all cases behave exactly like what a C# developer would expect from a serializer. Here we’ll describe how the serializer works and some best practices on how to make the best use of it.

What does a field of my script need to be in order to be serialized?

Which fieldtypes can we serialize?

So far so good. So what are these situations where the serializer behaves differently from what I expect?

Custom classes behave like structs

If you populate the animals array with three references to a single Animal object, in the serializationstream you will find 3 objects. When it’s deserialized, there are now three different objects. If you need to serialize a complex object graph with references, you cannot rely on Unity’s serializer doing that all automagically for you, and have to do some work to get that object graph serialized yourself. See the example below on how to serialize things Unity doesn’t serialize by itself.

Note that this is only true for custom classes, as they are serialized “inline” because their data becomes part of the complete serializationdata for the MonoBehaviour they are used in. When you have fields that have a reference to something that is a UnityEngine.Object derived class, like a “public Camera myCamera”, the data from that camera are not serialized inline, and an actual reference to the camera UnityEngine.Object is serialized.

No support for null for custom classes

Pop quiz. How many allocations are made when deserializing a MonoBehaviour that uses this script:

It wouldn’t be strange to expect 1 allocation, that of the Test object. It also wouldn’t be strange to expect 2 allocations, one for the Test object and one for a Trouble object. The correct answer is 729. The serializer does not support null. If it serializes an object and a field is null, we just instantiate a new object of that type and serialize that. Obviously this could lead to infinite cycles, so we have a relatively magical depth limit of 7 levels. At that point we just stop serializing fields that have types of custom classes/structs and lists and arrays. [1]

Since so many of our subsystems build on top of the serialization system, this unexpectedly big serializationstream for the Test monobehaviour will cause all these subsystems to perform more slowly than necessary. When we investigate performance problems in customer projects, we almost always find this problem and we added a warning for this situation in Unity 4.5. We actually messed up the warning implementation in such a way that it gives you so many warnings, you have no other option but to fix them right away. We’ll soon ship a fix for this in a patch release, the warning is not gone, but you will only get one per «entering playmode», so you don’t get spammed crazy. You’d still want to fix your code, but you should be able to do it at a time where it suits you.

No support for polymorphism

and you put in an instance of a dog, a cat and a giraffe, after serialization, you will have three instances of Animal.

One way to deal with this limitation is to realize that it only applies to “custom classes”, which get serialized inline. References to other UnityEngine.Object’s get serialized as actual references and for those, polymorphism does actually work. You’d make a ScriptableObject derived class or another MonoBehaviour derived class, and reference that. The downside of doing this, is that you need to store that monobehaviour or scriptable object somewhere and cannot serialize it inline nicely.

The reason for these limitations is that one of the core foundations of the serialization system is that the layout of the datastream for an object is known ahead of time, and depends on the types of the fields of the class, instead of what happens to be stored inside the fields.

I want to serialize something that Unity’s serializer doesn’t support. What do I do?

In many cases the best approach is to use serialization callbacks. They allow you to be notified before the serializer reads data from your fields and after it is done writing to them. You can use this to have a different representation of your hard-to-serialize data at runtime than when you actually serialize. You’d use these to transform your data into something Unity understands right before Unity wants to serialize it, you also use it to transform the serialized form back into the form you’d like to have your data in at runtime, right after Unity has written the data to your fields.

Let’s say you want to have a tree datastructure. If you let Unity directly serialize the data structure, the “no support for null” limitation would cause your datastream to become very big, leading to performance degradations in many systems:

Instead, you tell Unity not to serialize the tree directly, and you make a seperate field to store the tree in a serialized format, suited for Unity’s serializer:

Beware that the serializer, including these callbacks coming from the serializer, usually do not run on the main thread, so you are very limited in what you can do in terms of invoking Unity API. (Serialization happening as part of loading a scene happens on a loading thread. Serialization happening as part of you invoking Instantiate() from script happens on the main thread). You can however do the necessary data transformations do get your data from a non-unity-serializer-friendly format to a unity-serializer-friendly-format.

You made it to the end!

Thanks for reading this far, hope you can put some of this information to good use in your projects.

PS: We’ll add all this information to the documentation as well.

[1] I lied, the correct answer isn’t actually 729. This is because in the very very old days before we had this 7 level depth limit, Unity would just endless loop, and then run out of memory if you created a script like the Trouble one I just wrote. Our very first fix for that 5 years ago was to just not serialize fieldtypes that were of the same type as the class itself. Obviously, this was not the most robust fix, as it’s easy to create a cycle using Trouble1->Trouble2->Trouble1->Trouble2 class. So shortly afterwards we actually implemented the 7 level depth limit to catch those cases too. For the point I’m trying to make however it doesn’t matter, what matters is that you realize that if there is a cycle you are in trouble.

Источник

Бинарная сериализация в Unity 3D/Visual Studio Application

В процессе разработки плагина для Unity 3D понадобилось сделать хранение относительно большого количества данных. В моем случае это хранение данных нодов для визуального программирования (так же применим и к реализации сохранения игры). Способ хранения должен отвечать заданным требованиям:

Программа

unity system serializable что это. Смотреть фото unity system serializable что это. Смотреть картинку unity system serializable что это. Картинка про unity system serializable что это. Фото unity system serializable что это
(код программы в конце статьи)

Новая созданная нода должна добавляться в первую свободную позицию в коллекции, для этого я использовал код:

Сериализация

Два шага, которых нужно выполнить на данном этапе, это заставить сериализатор работать с нашим классом NodesV1 и сделать учет на то, что структура данных сериализуемого\ десериализуемого объекта будет меняться (в процессе разработки она будет изменяться не раз).

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

Для начала нужен класс, который будет работать над сериализацией/десериализацией, в нем же заставим сериализатор работать с нашим классом.

Сохранять файл будем в папку проекта Unity: Assets\Resources. Именно из папки Resources будет корректно работать на Unity чтение файла на мобильных устройствах и т. д.

Теперь шаг второй, решить вопрос с версией десериализатора. В первые два байта бинарного файла мы будем записывать версию сериализатора. При десериализации мы считываем версию, убираем эти два байта и запускаем десериализатор соответствующей версии. Версия сериализатора будет определяться по цифрам в конце имени класса (NodesV1 – версия ”1”).

Добавим проверку версии:

Теперь создадим в программе несколько нодов и запустим сериализацию. Полученный файл нам еще понадобится.
Теперь проверяем, работает ли. Допустим, наша структура изменилась, мы добавили переменную Permission (Perm). Создадим класс с новой структурой:

Изменяем в коде программы класс NodesV1 на NodesV2. При запуске так же инициализируем новую переменную:

Теперь самое интересное. В файле со старой структурой данных нет переменной Perm, а нам нужно десериализовать в соответствии со старой структурой и вернуть в новой.

В каждом случае будет происходить своя обработка этой ситуации, но у меня будет просто создаваться эта коллекция со значениями false.

Изменим код проверки версии в десериализаторе:

После изменений десериализация файла со старой структурой проходит успешно.

Unity

Как видим, код класса обрабатывающий сериализацию тот же самый, только вместо:

мы будем использовать:

Если скрипт не планируется запускать на мобильных устройствах (или аналогичных), можно ничего не трогать, только подправить пути:

После сохранения объектов кнопкой Save нужно свернуть и развернуть Unity, чтобы обновленный бинарный файл импортировался и обновился.

Теперь можно изменять и сохранять бинарный файл в программе и в юнити:

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *