Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Serialization Best Practices - Megapost

Discussion in 'Scripting' started by Tim-C, Oct 19, 2012.

  1. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,221
    Unity Serialization
    So you are writing a really cool editor extension in Unity and things seem to be going really well. You get your data structures all sorted out are really happy with how the tool you have written works.

    Then you enter and exit play mode.

    Suddenly all the data you had entered is gone and your tool is reset to the default, just initialized state. It’s very frustrating! “Why does this happen?” you ask yourself. The reason has to do with how the managed (mono) layer of Unity works. Once you understand it, then things get much easier :)

    What happens when an assembly is reloaded?
    When you enter / exit play mode or change a script Unity has to reload the mono assemblies, that is the dll's associated with Unity.

    On the user side this is a 3 step process:
    • Pull all the serializable data out of managed land, creating an internal representation of the data on the C++ side of Unity.
    • Destroy all memory / information associated with the managed side of Unity, and reload the assemblies.
    • Reserialize the data that was saved in C++ back into managed land.

    What this means is that for your data structures / information to survive an assembly reload you need to ensure that can get serialized into and out of c++ memory properly. Doing this also means that (with some minor modifications) you can save this data structure to an asset file and reload it at a later date.

    How do I work with Unity's serialization?
    The easiest way to learn about Unity serialization is by working through an example. We are going to start with a simple editor window, it contains a reference to a class which we want to make survive an assembly reload.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4.  
    5. public class MyWindow : EditorWindow
    6. {
    7.     private SerializeMe m_SerialziedThing;
    8.  
    9.     [MenuItem ("Window/Serialization")]
    10.     static void Init () {
    11.         GetWindow <MyWindow>();
    12.     }
    13.  
    14.     void OnEnable ()
    15.     {
    16.         hideFlags = HideFlags.HideAndDontSave;
    17.         if (m_SerialziedThing == null)
    18.             m_SerialziedThing = new SerializeMe ();
    19.     }
    20.  
    21.     void OnGUI () {
    22.         GUILayout.Label ("Serialized Things", EditorStyles.boldLabel);
    23.         m_SerialziedThing.OnGUI ();
    24.     }
    25. }
    26.  
    Code (csharp):
    1.  
    2. using UnityEditor;
    3.  
    4. public struct NestedStruct
    5. {
    6.     private float m_StructFloat;
    7.     public void OnGUI ()
    8.     {
    9.         m_StructFloat = EditorGUILayout.FloatField("Struct Float", m_StructFloat);
    10.     }
    11. }
    12.  
    13. public class SerializeMe
    14. {
    15.     private string m_Name;
    16.     private int m_Value;
    17.  
    18.     private NestedStruct m_Struct;
    19.  
    20.     public SerializeMe ()
    21.     {
    22.         m_Struct = new NestedStruct();
    23.         m_Name = "";
    24.     }
    25.  
    26.     public void OnGUI ()
    27.     {
    28.         m_Name = EditorGUILayout.TextField( "Name", m_Name);
    29.         m_Value = EditorGUILayout.IntSlider ("Value", m_Value, 0, 10);
    30.  
    31.         m_Struct.OnGUI ();
    32.     }
    33. }
    34.  
    When you run this and force an assembly reload you will notice that any value in the window you have changed will not survive. This is because when the assembly is reloaded the reference to the ‘m_SerialziedThing’ is gone. It is not marked up to be serialized.

    There are a few things that need to be done to make this serialization work properly:
    In MyWindow.cs:
    • The field ‘m_SerializedThing’ needs to have the attribute [SerializeField] added to it. What this tells Unity is that it should attempt to serialize this field on assembly reload or similar events.

    In SerializeMe.cs:
    • The class ‘SerializeMe’ needs to have the [Serializable] attribute added to it. This tells Unity that the class is serializable.
    • The struct ‘NestedStruct’ needs to have the [Serializable] attribute added to it.
    • Each (non public) field that you want to be serialized needs to have the [SerializeField] attribute added to it.

    After adding these flags open the window and modify the fields. You will notice that after an assembly reload that the fields retain their values; that is apart from the field that came from the struct. This brings up the first important point, structs are not very well supported for serialization. Changing ‘NestedStruct’ from a struct to a class fixes this issue.

    The code now looks like this:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4.  
    5. public class MyWindow : EditorWindow
    6. {
    7.     private SerializeMe m_SerialziedThing;
    8.  
    9.     [MenuItem ("Window/Serialization")]
    10.     static void Init () {
    11.         GetWindow <MyWindow>();
    12.     }
    13.  
    14.     void OnEnable ()
    15.     {
    16.         hideFlags = HideFlags.HideAndDontSave;
    17.         if (m_SerialziedThing == null)
    18.             m_SerialziedThing = new SerializeMe ();
    19.     }
    20.  
    21.     void OnGUI () {
    22.         GUILayout.Label ("Serialized Things", EditorStyles.boldLabel);
    23.         m_SerialziedThing.OnGUI ();
    24.     }
    25. }
    26.  
    27. using System;
    28. using UnityEditor;
    29. using UnityEngine;
    30.  
    31. [Serializable]
    32. public class NestedStruct
    33. {
    34.     [SerializeField]
    35.     private float m_StructFloat;
    36.     public void OnGUI ()
    37.     {
    38.         m_StructFloat = EditorGUILayout.FloatField("Struct Float", m_StructFloat);
    39.     }
    40. }
    41.  
    42. [Serializable]
    43. public class SerializeMe
    44. {
    45.     [SerializeField]
    46.     private string m_Name;
    47.     [SerializeField]
    48.     private int m_Value;
    49.     [SerializeField]
    50.     private NestedStruct m_Struct;
    51.  
    52.     public SerializeMe ()
    53.     {
    54.         m_Struct = new NestedStruct();
    55.         m_Name = "";
    56.     }
    57.  
    58.     public void OnGUI ()
    59.     {
    60.         m_Name = EditorGUILayout.TextField( "Name", m_Name);
    61.         m_Value = EditorGUILayout.IntSlider ("Value", m_Value, 0, 10);
    62.  
    63.         m_Struct.OnGUI ();
    64.     }
    65. }
    66.  
    Some Serialization Rules
    • Avoid structs
    • Classes you want to be serializable need to be marked with [Serializable]
    • Public fields are serialized (so long as they reference a [Serializable] class)
    • Private fields are serialized under some circumstances (editor).
    • Mark private fields as [SerializeField] if you wish them to be serialized.
    • [NonSerialized] exists for fields that you do not want to serialize

    Scriptable Objects
    So far we have looked at using normal classes when it comes to serialization. Unfortunately using plain classes has some issues when it comes to serialization in Unity. Lets take a look at an example.

    Code (csharp):
    1.  
    2. using System;
    3. using UnityEditor;
    4. using UnityEngine;
    5.  
    6. [Serializable]
    7. public class NestedClass
    8. {
    9.     [SerializeField]
    10.     private float m_StructFloat;
    11.     public void OnGUI()
    12.     {
    13.         m_StructFloat = EditorGUILayout.FloatField("Float", m_StructFloat);
    14.     }
    15. }
    16.  
    17. [Serializable]
    18. public class SerializeMe
    19. {
    20.     [SerializeField]
    21.     private NestedClass m_Class1;
    22.  
    23.     [SerializeField]
    24.     private NestedClass m_Class2;
    25.  
    26.     public void OnGUI ()
    27.     {
    28.         if (m_Class1 == null)
    29.             m_Class1 = new NestedClass ();
    30.         if (m_Class2 == null)
    31.             m_Class2 = m_Class1;
    32.  
    33.         m_Class1.OnGUI();
    34.         m_Class2.OnGUI();
    35.     }
    36. }
    37.  
    This is a contrived example to show a very specific corner case of the Unity serialization system that can catch you if you are not careful. You will notice that we have two fields of type NestedClass. The first time the window is drawn it will show both the fields, and as m_Class1 and m_Class2 point to the same reference, modifying one will modify the other.

    Now try reloading the assembly by entering and exiting play mode... The references have been decoupled. This is due to how serialization of works when you mark a class as simply [Serializable]

    When you are serializing standard classes Unity walks through the fields of the class and serializes each one individually, even if the reference is shared between multiple fields. This means that you could have the same object serialized multiple times, and on deserialization the system will not know they are really the same object. If you are designing a complex system this is a frustrating limitation because it means that complex interactions between classes can not be captured properly.

    Enter ScriptableObjects! ScriptableObjects are a type of class that correctly serializes as references, so that they only get serialized once. This allows complex class interactions to be stored in a way that you would expect. Internally in Unity ScriptableObjects and MonoBehaviours are the same; in userland code you can have a ScriptableObject that is not attached to a GameObject; this is different to how MonoBehaviour works. They are great for general data structure serialization.

    Let’s modify the example to be able to handle serialization properly:

    Code (csharp):
    1.  
    2. using System;
    3. using UnityEditor;
    4. using UnityEngine;
    5.  
    6. [Serializable]
    7. public class NestedClass : ScriptableObject
    8. {
    9.     [SerializeField]
    10.     private float m_StructFloat;
    11.  
    12.     public void OnEnable() { hideFlags = HideFlags.HideAndDontSave; }
    13.  
    14.     public void OnGUI()
    15.     {
    16.         m_StructFloat = EditorGUILayout.FloatField("Float", m_StructFloat);
    17.     }
    18. }
    19.  
    20. [Serializable]
    21. public class SerializeMe
    22. {
    23.     [SerializeField]
    24.     private NestedClass m_Class1;
    25.  
    26.     [SerializeField]
    27.     private NestedClass m_Class2;
    28.  
    29.     public SerializeMe ()
    30.     {
    31.         m_Class1 = ScriptableObject.CreateInstance<NestedClass> ();
    32.         m_Class2 = m_Class1;
    33.     }
    34.  
    35.     public void OnGUI ()
    36.     {
    37.         m_Class1.OnGUI();
    38.         m_Class2.OnGUI();
    39.     }
    40. }
    41.  
    The three changes of note here are that:
    • NestedClass is now a ScriptableObject.
    • We create an instance using the CreateInstance<> function instead of calling the constructor.
    • We also set the hide flags... this will be explained later

    These simple changes mean that the instance of the NestedClass will only be serialized once, with each of the references to the class pointing to the same one.

    ScriptableObject Initialization
    So now we know that for complex data structures where external referencing is needed it is a good idea to use ScriptableObjects. But what is the correct way to work with ScriptableObjects from user code? The first thing to examine is HOW scriptable objects are initialized, especially from the Unity serialization system.
    1. The constructor is called on the ScriptableObject
    2. Data is serialized into the object from the c++ side of unity (if such data exists)
    3. OnEnable() is called on the ScriptableObject

    Working with this knowledge there are some things that we can say:
    • Doing initialization in the constructor isn’t a very good idea as data will potentially be overridden by the serialization system.
    • Serialization happens AFTER construction, so we should do our configuration stuff after serialization.
    • OnEnable() seems like the best candidate for initialization.

    Lets make some changes to the ‘SerializeMe’ class so that it is a ScriptableObject. This will allow us to see the correct initialization pattern for ScriptableObjects.

    Code (csharp):
    1.  
    2. // also updated the Window to call CreateInstance instead of the constructor
    3. using System;
    4. using UnityEngine;
    5.  
    6. [Serializable]
    7. public class SerializeMe : ScriptableObject
    8. {
    9.     [SerializeField]
    10.     private NestedClass m_Class1;
    11.  
    12.     [SerializeField]
    13.     private NestedClass m_Class2;
    14.  
    15.     public void OnEnable ()
    16.     {
    17.         hideFlags = HideFlags.HideAndDontSave;
    18.         if (m_Class1 == null)
    19.         {
    20.             m_Class1 = CreateInstance<NestedClass> ();
    21.             m_Class2 = m_Class1;
    22.         }
    23.     }
    24.  
    25.     public void OnGUI ()
    26.     {
    27.         m_Class1.OnGUI();
    28.         m_Class2.OnGUI();
    29.     }
    30. }
    31.  
    On the surface it seems that we have not really changed this class much, it now inherits from ScriptableObject and instead of using a constructor has an OnEnable(). The important part to take note of is slightly more subtle... OnEnable() is called AFTER serialization; because of this we can see if the [SerializedFields] are null or not. If they are null it indicates that this the first initialization, and we need to construct the instances. If they are not null then they have been loaded into memory, and do NOT need to be constructed. It is common in OnEnable() to also call a custom Initialization function to configure any private / non serialized fields on the object, much like you would do in a constructor.

    HideFlags
    In the examples using ScriptableObjects you will notice that we are setting the ‘hideFlags’ on the object to HideFlags.HideAndDontSave. This is a special setup that is required when writing custom data structures that have no root in the scene. This is to get around how the Garbage Collector works in Unity.

    When the garbage collector is run it (for the most part) uses the scene as ‘the root’ and traverses the hierarchy to see what can get GC’d. Setting the HideAndDontSave flag on a ScriptableObject tells Unity to consider that object as a root object. Because of this it will not just disappear because of a GC / assembly reload. The object can still be destroyed by calling Destroy().

    Some ScriptableObject Rules
    • ScriptableObjects will only be serialized once, allowing you to use references properly
    • Use OnEnable to initialize ScriptableObjects
    • Don’t ever call the constructor of a ScriptableObject, use CreatInstance instead.
    • For nested data structures that are only referenced once don’t use ScriptableObject as they have more overhead.
    • If your scriptable object is not rooted in the scene set the hideFlags to HideAndDontSave

    Concrete Array Serialization
    Lets have a look at a simple example that serializes a range of concrete classes.

    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using UnityEditor;
    5. using UnityEngine;
    6.  
    7. [Serializable]
    8. public class BaseClass
    9. {
    10.     [SerializeField]
    11.     private int m_IntField;
    12.     public void OnGUI() {m_IntField = EditorGUILayout.IntSlider ("IntField", m_IntField, 0, 10);}
    13. }
    14.  
    15. [Serializable]
    16. public class SerializeMe : ScriptableObject
    17. {
    18.     [SerializeField]
    19.     private List<BaseClass> m_Instances;
    20.  
    21.     public void OnEnable ()
    22.     {
    23.         hideFlags = HideFlags.HideAndDontSave;
    24.         if (m_Instances == null)
    25.             m_Instances = new List<BaseClass> ();
    26.     }
    27.  
    28.     public void OnGUI ()
    29.     {
    30.         foreach (var instance in m_Instances)
    31.             instance.OnGUI ();
    32.  
    33.         if (GUILayout.Button ("Add Simple"))
    34.             m_Instances.Add (new BaseClass ());
    35.     }
    36. }
    37.  
    This basic example has a list of BaseClasses, by clicking the ‘Add Simple’ button it creates an instance and adds it to the list. Due to the SerializeMe class being configured properly for serialization (as discussed before) it ‘just works’. Unity sees that the List is marked for serialization and serializes each of the List elements.

    General Array Serialization
    Lets modify the example to serialize a list that contains members of a base class and child class:

    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using UnityEditor;
    5. using UnityEngine;
    6.  
    7. [Serializable]
    8. public class BaseClass
    9. {
    10.     [SerializeField]
    11.     private int m_IntField;
    12.     public virtual void OnGUI() { m_IntField = EditorGUILayout.IntSlider("IntField", m_IntField, 0, 10); }
    13. }
    14.  
    15. [Serializable]
    16. public class ChildClass : BaseClass
    17. {
    18.     [SerializeField]
    19.     private float m_FloatField;
    20.     public override void OnGUI()
    21.     {
    22.         base.OnGUI ();
    23.         m_FloatField = EditorGUILayout.Slider("FloatField", m_FloatField, 0f, 10f);
    24.     }
    25. }
    26.  
    27. [Serializable]
    28. public class SerializeMe : ScriptableObject
    29. {
    30.     [SerializeField]
    31.     private List<BaseClass> m_Instances;
    32.  
    33.     public void OnEnable ()
    34.     {
    35.         if (m_Instances == null)
    36.             m_Instances = new List<BaseClass> ();
    37.  
    38.         hideFlags = HideFlags.HideAndDontSave;
    39.     }
    40.  
    41.     public void OnGUI ()
    42.     {
    43.         foreach (var instance in m_Instances)
    44.             instance.OnGUI ();
    45.  
    46.         if (GUILayout.Button ("Add Base"))
    47.             m_Instances.Add (new BaseClass ());
    48.         if (GUILayout.Button ("Add Child"))
    49.             m_Instances.Add (new ChildClass ());
    50.     }
    51. }
    52.  
    The example has been extended so that there is now a ChildClass, but we are serializing using the BaseClass. If you create a few instance of the ChildClass and the BaseClass they will render properly. Issues arise when they are placed through an assembly reload. After the reload completes every instance will be a BaseClass, with all the ChildClass information stripped. The instances are being sheared by the serialization system.

    The way to work around this limitation of the serialization system is to once again use ScriptableObjects:

    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEditor;
    6.  
    7. [Serializable]
    8. public class MyBaseClass : ScriptableObject
    9. {
    10.     [SerializeField]
    11.     protected int m_IntField;
    12.  
    13.     public void OnEnable() { hideFlags = HideFlags.HideAndDontSave; }
    14.  
    15.     public virtual void OnGUI ()
    16.     {
    17.         m_IntField = EditorGUILayout.IntSlider("IntField", m_IntField, 0, 10);
    18.     }
    19. }
    20.  
    21. [Serializable]
    22. public class ChildClass : MyBaseClass
    23. {
    24.     [SerializeField]
    25.     private float m_FloatField;
    26.  
    27.     public override void OnGUI()
    28.     {
    29.         base.OnGUI ();
    30.         m_FloatField = EditorGUILayout.Slider("FloatField", m_FloatField, 0f, 10f);
    31.     }
    32. }
    33.  
    34. [Serializable]
    35. public class SerializeMe : ScriptableObject
    36. {
    37.     [SerializeField]
    38.     private List<MyBaseClass> m_Instances;
    39.  
    40.     public void OnEnable ()
    41.     {
    42.         if (m_Instances == null)
    43.             m_Instances = new List<MyBaseClass>();
    44.  
    45.         hideFlags = HideFlags.HideAndDontSave;
    46.     }
    47.  
    48.     public void OnGUI ()
    49.     {
    50.         foreach (var instance in m_Instances)
    51.             instance.OnGUI ();
    52.  
    53.         if (GUILayout.Button ("Add Base"))
    54.             m_Instances.Add(CreateInstance<MyBaseClass>());
    55.         if (GUILayout.Button ("Add Child"))
    56.             m_Instances.Add(CreateInstance<ChildClass>());
    57.     }
    58. }
    59.  
    After running this, changing some values, and reloading assemblies you will notice that ScriptableObjects are safe to use in arrays even if you are serializing derived types. The reason is that when you serialize a standard [Serializable] class it is serialized ‘in place’, but a ScriptableObject is serialized externally and the reference inserted into the collection. The shearing occurs because the type can not be properly be serialized as the serialization system thinks it is of the base type.

    Serializing Abstract Classes
    So now we have seen that it’s possible to serialize a general list (so long as the members are of type ScriptableObject). Lets see how abstract classes behave:

    Code (csharp):
    1.  
    2. using System;
    3. using UnityEditor;
    4. using System.Collections.Generic;
    5. using UnityEngine;
    6.  
    7. [Serializable]
    8. public abstract class MyBaseClass : ScriptableObject
    9. {
    10.     [SerializeField]
    11.     protected int m_IntField;
    12.  
    13.     public void OnEnable() { hideFlags = HideFlags.HideAndDontSave; }
    14.  
    15.     public abstract void OnGUI ();
    16. }
    17.  
    18. [Serializable]
    19. public class ChildClass : MyBaseClass
    20. {
    21.     [SerializeField]
    22.     private float m_FloatField;
    23.  
    24.     public override void OnGUI()
    25.     {
    26.         m_IntField = EditorGUILayout.IntSlider("IntField", m_IntField, 0, 10);
    27.         m_FloatField = EditorGUILayout.Slider("FloatField", m_FloatField, 0f, 10f);
    28.     }
    29. }
    30.  
    31. [Serializable]
    32. public class SerializeMe : ScriptableObject
    33. {
    34.     [SerializeField]
    35.     private List<MyBaseClass> m_Instances;
    36.  
    37.     public void OnEnable ()
    38.     {
    39.         if (m_Instances == null)
    40.             m_Instances = new List<MyBaseClass>();
    41.  
    42.         hideFlags = HideFlags.HideAndDontSave;
    43.     }
    44.  
    45.     public void OnGUI ()
    46.     {
    47.         foreach (var instance in m_Instances)
    48.             instance.OnGUI ();
    49.  
    50.         if (GUILayout.Button ("Add Child"))  
    51.             m_Instances.Add(CreateInstance<ChildClass>());
    52.     }
    53. }
    54.  
    This code much like the previous example works. But it IS dangerous. Lets see why.

    The function CreateInstance<>() expects a type that inherits from ScriptableObject, the class ‘MyBaseClass’ does in fact inherit from ScriptableObject. This means that it’s possible to add an instance of the abstract class MyBaseClass to the m_Instances array. If you do this and then try and access an abstract method bad things will happen because there is no implementation of that function. In this specific case that would be the OnGUI method.

    Using abstract classes as the serialized type for lists and fields DOES work, so long as they inherit from ScriptableObject, but it is not a recommended practice. Personally I think it’s better to use concrete classes with empty virtual methods. This ensures that things will not go bad for you.

    Still to come
    • A few handy tips and tricks
    • How to save your serialized data as an asset

    Questions?
     
    NotaNaN, TiagoB66, tinyant and 33 others like this.
  2. Landern

    Landern

    Joined:
    Dec 21, 2008
    Posts:
    354
    Wow, nice gift to the forum Stramit!
     
  3. Cominu

    Cominu

    Joined:
    Feb 18, 2010
    Posts:
    149
    Thanks Stramit, your post is pure gold :D
     
  4. w00dn

    w00dn

    Joined:
    Apr 28, 2010
    Posts:
    275
    Definitely a nice gift, thanks! That clears up a few things in my brain. :)
     
  5. Nevermind

    Nevermind

    Joined:
    Jun 10, 2010
    Posts:
    66
    This is super mega awesome! But it should also be on wiki and in Unity docs.
     
  6. Hoegbo

    Hoegbo

    Joined:
    Jan 21, 2012
    Posts:
    62
    This is really awesome, thanks for post :)
    Perhaps this could also be posted on the wiki.
     
  7. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,649
    I think you've used "SerializedObject" in a few places where you meant "ScriptableObject."

    WRT HideFlags: that's interesting, didn't know that about the GC. What about SOs that are persisted asset files? I guess that if they aren't referenced in the scene, they're still unloaded by the GC in the same way, but can be reloaded again by opening the asset same as any other resource?
     
  8. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,221
    Ah thanks :) In my head I'm always thinking about them from a serialization standpoint :)

    I'll get to this when I write about using SO's as assets, which I'll do tomorrow or Monday.
     
  9. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,649
    Cool. Clarification on when ScriptableObjects get persisted into scene/prefab files (versus just being lost to the ether when closing Unity) would be especially welcome.
     
  10. aaronflippo

    aaronflippo

    Joined:
    Jun 7, 2012
    Posts:
    45
    This post is pretty much the exact thing I've been searching the entire internet all day for. Thank you!
     
  11. whydoidoit

    whydoidoit

    Joined:
    Mar 12, 2012
    Posts:
    365
    Very helpful, thanks for writing this!
     
  12. SirEel

    SirEel

    Joined:
    Oct 8, 2012
    Posts:
    3
    Good post, but I'm having a little trouble:

    My code has type Foo : ScriptableObject, which gets saved as an asset file, and contains a List of abstract bar : ScriptableObject, which really contains some derived types.

    This mostly works, except that on reloading the editor, the entries in the List will all be null. I think the only deviation from what you described I've made is to not set the hideflags in Foo, because this messes with the asset creation (and I don't think it's needed for something that lives in an asset file anyway).

    Any ideas would be greatly appreciated.

    If needed I can construct a minimal repro case tomorrow night to show what I mean, but I'm about to head to bed right now!

    Edit:
    Suddenly I notice that what I want to do is in your "Still to Come" list, I guess I'll have to see if I can figure it out before you post that!
     
    Last edited: Oct 24, 2012
  13. aaronflippo

    aaronflippo

    Joined:
    Jun 7, 2012
    Posts:
    45
    Eagerly awaiting your post on Saving/Loading :)

    I'm using these principles to build the basics of an editor that I'm wrapping in both a Unity custom editor, and an in-game editor.

    It seems to me that there are a couple different ways of handling save/load: The way handled in this thread using subclasses of ISerializable:

    http://answers.unity3d.com/question...game-option.html?page=1&pageSize=5&sort=votes

    And the alternate method of using AssetDatabase.CreateAsset().

    I'm curious what the differences are. Obviously the latter won't work outside of the context of the editor...
     
    Last edited: Oct 24, 2012
  14. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,221
    I would love a minimal repro case, just so I can be sure to cover your specific issue when I cover the save / load section.

    This post has been updated and posted on the Unity blog. Please check out the clarification to 'HideFlags' and the answer to the question about when serialized objects get saved if they are part of the scene.
     
  15. SirEel

    SirEel

    Joined:
    Oct 8, 2012
    Posts:
    3
    I'll start working on it on my lunch break, and post it for you tonight (after 18:00GMT)

    I've got to admit it's weird being an old hand at programming, and picking up another new engine, makes me feel like a noob again!
     
  16. SirEel

    SirEel

    Joined:
    Oct 8, 2012
    Posts:
    3
    View attachment $SerialisationMinimalCase.zip View attachment $SerialisationMinimalCase.zip
    Ok! My minimal case and the case I have in my project currently fail in very slightly different ways, but the approach and general structure are identical. Attached a zip of the complete project as it was pretty small.

    Hopefully I won't have embarrassed myself with how bad the approach is; I'm new to unity and still not so familiar with C# (never worked on anything in it for longer than a day or so). PM me or message here if you want to know anything extra about it.
     
  17. nuverian

    nuverian

    Joined:
    Oct 3, 2011
    Posts:
    2,087
    I have 2 quetions regarding:

    A) One thing I dont understand is why not create an instance of a ScriptableObject derived calss by calling -new TheClass(x,y,);-?
    So that we are able to pass arguments to the contructor instead of the workaround way for faking the constructor. Plus it seems to work fine, apart from the warning in the console. So what it that actually will make me not use ScriptableObjects in such a way?

    B) I have a Monobehaviour script which has a variable of a ScriptableObject derived class. The MonoBehaviour is a prefab. When I create an instance of the ScriptableObject and put it in the variable while inspecting the prefab itself, I get "type mismatch" and of course at PLAY it goes to "missing" (at second PLAY it goes to "none").

    Thanks,
    Evan
     
  18. Futurerobot

    Futurerobot

    Joined:
    Jul 13, 2011
    Posts:
    179
    That was some cool stuff, always wondered about this. I'm having problems with some serialization right now myself, but I'm not sure if I found the answer I was looking for in there, maybe I missed it.

    I have a monobehaviour class, ValueHolder, assigned to an object in the game, holding various values I reference. I have them there because of the easy access to the inspector for tweaking while the game is running.

    An editorscript, ValueMaker, populates all the values in ValueHolder, serializes it and saves to a text file. ValueHolder loads this and deserializes them back in "Awake()".

    The odd behaviour I'm seeing is:
    I've at some point in the past run ValueMaker and generated all the values for the ValueHolder, the values look fine in the inspector. (values A)
    I go into some of the values, and change them to bogus values. (values B)
    I edit ValueMaker to generate a different set of values and run it, the inspector updates to show the new values. (Values C).
    I start the game, the values return to Values A. Not using the ones I serialized, not the ones I just typed in, but previous values.
    I stop the game, the values return to Values B. Not the ones I just put in the inspector with ValueMaker before starting the game, but the bogus ones I typed in before that.
     
    Last edited: Dec 6, 2012
  19. devain

    devain

    Joined:
    Jul 7, 2012
    Posts:
    14
    I'm also having this exact issue. This is a great post overall, but it would be superb if someone could point to where I misunderstood it.
     
  20. hww

    hww

    Joined:
    Nov 23, 2007
    Posts:
    58
    Lest imagine the MonoBehaviour which uses SerializeMe as the property.

    Code (csharp):
    1.  
    2. class MyBehaviour : MonoBehaviour {
    3.    public SerializeMe serializeMe;
    4. }
    5.  
    Also we make the custom editor for MyBehaviour class. And the custom editor could fill the serializeMe with data. We also want create a prefab of the GameObject which has MyBehaviour component.

    1. We use custom editor to update elements of serializeMe property. We do not hit the Apply button. Nonetheless, we see that prefab is updated.

    2. We hit "apply" at the Inspector, the data of serializeMe should update the prefab. But prefab's data at the field serializeMe is reset to zero;

    3. When ispector renders the GUI elements for editing serializeMe field, it should figure out which fields differ from prefab (and display them with bold font)

    Does anyone has a solution for problems 1 and 2 ?

    There is little project which shows the issue. Open schene remove Prefab from scene, then select prefab in the project and "Add Number", "Add Message" in the inspector. Then drop prefab to the scene. And try change any number or message value in the inspector, reffer to what will happen with prefab. (it should have updates).
    Now click Apply in inspector and reffer to Prefab, the properties are deleted!


    View attachment $SerialisationMinimalCase.zip
     
    Last edited: Mar 5, 2013
  21. hww

    hww

    Joined:
    Nov 23, 2007
    Posts:
    58
    Similar issue explained here HERE

     
  22. hww

    hww

    Joined:
    Nov 23, 2007
    Posts:
    58
    According to this [post] we can say that Unity does not alow to do it. That is bad. :(
     
  23. whydoidoit

    whydoidoit

    Joined:
    Mar 12, 2012
    Posts:
    365
    You need the item to be a scriptable object because of slicing (polymorphism)? You could always create an asset out of the scriptable object for each unique one you create, then Instantiate a copy of it when the application is running if each instance of the prefab needs its own copy.
     
  24. hww

    hww

    Joined:
    Nov 23, 2007
    Posts:
    58
    Yes

    It is not the way because requires too many assets.

    Would be nice if i coud define the class (not based on ScriptableObject or MonoBehaviour) and implement the serialization interface which will be used by Unity editor.

    Code (csharp):
    1.  
    2. class Foo : ISerializationInterface
    3. {
    4.    void Serialize(Stream s) {
    5.      
    6.    }
    7.    void Deserialize(Stream s) {
    8.      
    9.    }
    10. }
    11.  
    Is it possible?
     
  25. Spidyy

    Spidyy

    Joined:
    Mar 6, 2011
    Posts:
    184
    I got stuck by something from your code for the Concrete Serialization. Here the example you gave :

    Code (csharp):
    1. using System;
    2.  
    3. using System.Collections.Generic;
    4.  
    5. using UnityEngine;
    6.  
    7. using UnityEditor;
    8.  
    9.  
    10.  
    11. [Serializable]
    12.  
    13. public class MyBaseClass : ScriptableObject
    14.  
    15. {
    16.  
    17.     [SerializeField]
    18.  
    19.     protected int m_IntField;
    20.  
    21.  
    22.  
    23.     public void OnEnable() { hideFlags = HideFlags.HideAndDontSave; }
    24.  
    25.  
    26.  
    27.     public virtual void OnGUI ()
    28.  
    29.     {
    30.  
    31.         m_IntField = EditorGUILayout.IntSlider("IntField", m_IntField, 0, 10);
    32.  
    33.     }
    34.  
    35. }
    36.  
    37.  
    38.  
    39. [Serializable]
    40.  
    41. public class ChildClass : MyBaseClass
    42.  
    43. {
    44.  
    45.     [SerializeField]
    46.  
    47.     private float m_FloatField;
    48.  
    49.  
    50.  
    51.     public override void OnGUI()
    52.  
    53.     {
    54.  
    55.         base.OnGUI ();
    56.  
    57.         m_FloatField = EditorGUILayout.Slider("FloatField", m_FloatField, 0f, 10f);
    58.  
    59.     }
    60.  
    61. }
    You said we need to use the OnEnabled() function to initialize our properties inside the Base class, but what about the Child class? What would I need to write if I want the m_FloatField initialized at 10f for example?
     
  26. yumupdate

    yumupdate

    Joined:
    Nov 20, 2012
    Posts:
    30
    hww, I'm having the same issue. Almost exactly as you described. I have not found a solution yet, but I'm trying frantically. Did you manage to work through your problem?
     
  27. sgoodrow

    sgoodrow

    Joined:
    Sep 28, 2012
    Posts:
    150
    This has been a big help -- thanks. Still a bit stuck, though.

    I am working on a ScriptableObject which has nested ScriptableObjects that are polymorphic, as the example includes. I've written a custom inspector for my primary ScriptableObject and that works great, allowing me to remove UnityEditor as a dependency for that asset. How do I do this for the nested assets? In your example, you call OnGui() of the nested assets, and these methods depend on UnityEditor. How would I get around this dependency?

    Or... do I not need to be worried about it, since the only object I am going to be passing around at runtime is the primary object, which has already been fully constructed in the editor?

    Thanks.
     
  28. Andy2222

    Andy2222

    Joined:
    Nov 4, 2009
    Posts:
    81
  29. jacksmash2012

    jacksmash2012

    Joined:
    Sep 21, 2010
    Posts:
    27
    I've found a couple of examples from the code above that crashes Unity. I've submitted a bug report, although I'm not sure if it's a Unity issue or not. Although I've been fooling around with the code a bit, I did attempt a copy in paste of the following, which crashes Unity:

    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using UnityEditor;
    5. using UnityEngine;
    6.  
    7. [Serializable]
    8. public class MyBaseClass : ScriptableObject
    9. {
    10.     [SerializeField]
    11.     private int m_IntField;
    12.  
    13.     public void OnEnable() { hideFlags = HideFlags.HideAndDontSave; }
    14.  
    15.     public virtual void OnGUI() {m_IntField = EditorGUILayout.IntSlider ("IntField", m_IntField, 0, 10);}
    16. }
    17.  
    18. [Serializable]
    19. public class ChildClass : MyBaseClass
    20. {
    21.     [SerializeField]
    22.     private float mFloat;
    23.     public override void OnGUI()
    24.     {
    25.         base.OnGUI();
    26.         mFloat = EditorGUILayout.Slider("FloatField", mFloat, 0f, 10f);
    27.     }
    28. }
    29.  
    30. [Serializable]
    31. public class SerializeMe : ScriptableObject
    32. {
    33.     [SerializeField]
    34.     private List<MyBaseClass> m_Instances;
    35.  
    36.     public void OnEnable ()
    37.     {
    38.         hideFlags = HideFlags.HideAndDontSave;
    39.         if (m_Instances == null)
    40.             m_Instances = new List<MyBaseClass> ();
    41.     }
    42.  
    43.     public void OnGUI ()
    44.     {
    45.         foreach (var instance in m_Instances)
    46.             instance.OnGUI ();
    47.  
    48.         if (GUILayout.Button("Add Base"))
    49.             m_Instances.Add(CreateInstance<MyBaseClass>());
    50.         if (GUILayout.Button ("Add Child"))
    51.             m_Instances.Add(CreateInstance<ChildClass>());
    52.     }
    53. }
     
  30. zombience

    zombience

    Joined:
    Mar 29, 2012
    Posts:
    57
    I've found that System.Type fields do not serialize properly.

    I have the following code:

    Code (csharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6.  
    7.  
    8. [Serializable]
    9. public class TypeStorage : ScriptableObject
    10. {
    11.     [SerializeField]
    12.     public Type type;
    13.     [SerializeField]
    14.     public string typeName;
    15.  
    16.     protected void OnEnable() { hideFlags = HideFlags.HideAndDontSave; }
    17.  
    18.     public virtual void OnGUI()
    19.     {
    20.         if(type != null)
    21.             GUILayout.Label(type.ToString(), EditorStyles.whiteLabel);
    22.         else
    23.             GUILayout.Label("no type set");
    24.        
    25.         GUILayout.Label(typeName);
    26.     }
    the typeName field serializes properly, but the type field is lost every time.

    is there a way around this? what is inherently different about System.Type that it cannot be serialized?
    I can work around it with string comparisons, but it would be much easier if Type were serializable.

    I've posted this over at unityanswers:
    http://answers.unity3d.com/questions/536916/cannot-serialize-systemtype-field.html
     
  31. jwyoo_stone

    jwyoo_stone

    Joined:
    Jul 15, 2012
    Posts:
    12
    Cool!

    Possible to serialize LinkedList<T> ?
     
  32. Omberone

    Omberone

    Joined:
    Jul 29, 2013
    Posts:
    3
    Dear lord this problem is nagging the hell out of me...
    I'm trying with LinkedList<T> now.
     
  33. Omberone

    Omberone

    Joined:
    Jul 29, 2013
    Posts:
    3
    Nope, same issue with LinkedList. I'm going to try to write my own serializer now and force it on top of unity's assets. I'll have a look and see if I need to write a custom deserializer as well or if I can somehow integrate Unity's deserializer.
     
  34. Omberone

    Omberone

    Joined:
    Jul 29, 2013
    Posts:
    3
    It can all be resolved with a small hack which I'm not going to use personally. If you put your List<T> in a seperate ScriptableObject class and store it in your monobehaviour you can add and edit your list and it will serialize.
     
  35. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,123
    Hey guys, quick question. I have an editor window that has references to a monobehaviour in the current scene.

    How can I keep that reference alive between assembly reloads (When the game is 'played' and 'stopped'?)? I *think* this question is on topic :p
     
    Last edited: Feb 14, 2014
  36. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,614
    Short answer: you can't, because it's no longer the same object afterwards. It is instead a fresh but identical object.

    As far as I know object IDs don't persist either, so that won't help...

    So the first potential solution to jump to my mind is storing the object's path and re-selecting it via that path. Of course there are cases where that won't work either, though, like multiple objects with the same path.


    Referring to the OP, it's stated that there's additional info in a blog post but there's no link? That'd be handy...

    Edit: Here it is.
     
    Last edited: Mar 17, 2014
  37. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    I had that issue, and fixed it by assigning a unique persisting ID to each GameObject of a scene. Anytime the hierarchy changes while the editor is not playing, a script assign the ID to every GameObject.

    Between play mode change, the EditorWindow retrieve the last GameObject's ID it was editing from the current scene.

    This "Metadata" script is flagged to not show up on the Inspector, to not clog it.
     
    Last edited: Mar 17, 2014
  38. RC-1290

    RC-1290

    Joined:
    Jul 2, 2012
    Posts:
    639
    I had the same problem, and it took me a while to create a work-around. Luckily, it is possible.

    It appears to me that during an assembly reload, component instances are replaced with a new instance that use the same InstanceID. But unlike other objects, the original instance continues to exist and even if you don't set the hideflags you can still access its members (Tenebrous suggested they just pretend to be null).

    Luckily, the gameObject property DOES point to the new GameObject. So you can get a reference to the new component instances, and find the one with the matching InstanceID.

    I've written an extension method for Component that should allow you to find the matching new instance:

    Code (csharp):
    1. /// <summary>
    2. /// Attempts to find the component that replaces the current component after an assembly reload.
    3. /// When an assembly is reloaded, new instances are created with matching instance ID's.
    4. /// Component references aren't restored correctly, but GameObjects are.
    5. /// This allows the new reference to be found.
    6. /// </summary>
    7. /// <returns>
    8. /// The component instance that replaces the given component after an assembly reload.
    9. /// </returns>
    10. /// <param name='originalComponent'>
    11. /// The component for which you want to find the replacement.
    12. /// </param>
    13. /// <exception cref='MissingComponentException'>
    14. /// Is thrown when none of the found components use the same instanceID.
    15. /// </exception>
    16. public static Component FindPostAssemblyReloadComponent(this Component originalComponent){
    17.     int originalID = originalComponent.GetInstanceID();
    18.     Type componentType = originalComponent.GetType();
    19.    
    20.     // There might be more components of the same type:
    21.     Component[] matchingComponents = originalComponent.gameObject.GetComponents(componentType);
    22.     for (int i = 0; i < matchingComponents.Length; i++) {
    23.         int foundID = matchingComponents[i].GetInstanceID();
    24.         if (foundID == originalID){ // A regular comparison between the instances would give 'False'
    25.             return matchingComponents[i];
    26.         }
    27.     }
    28.     throw new MissingComponentException("Can't find a component of same type with matching InstanceID");
    29. }
    The cost of calling this method isn't trivial, because you need to find several components, so it's best to only call it when necessary.
    I assume there might be a few different ways to detect an assembly reload. The system I'm using right now is to use a boolean that defaults back to true after an assembly reload:

    Code (csharp):
    1. [System.NonSerialized] private bool componentReferenceRequiresRestore = true;
    2. [SerializeField] private Component m_componentReference;
    3. public Component componentReference{
    4.     get {
    5.         if (componentReferenceRequiresRestore){
    6.             m_componentReference = m_componentReference.FindPostAssemblyReloadComponent();
    7.             componentReferenceRequiresRestore = false;
    8.         }
    9.         return m_componentReference;
    10.     }
    11. }
    Unfortunately this only detects going into play mode, not returning back from it, so in that case you're still left with a reference to the playmode.


    Does that help to solve your problems? Please let me know if you have suggestions for improvements.
     
  39. amirebrahimi

    amirebrahimi

    Joined:
    Jan 27, 2008
    Posts:
    171
    I came across this post when searching for how to have public properties properly used from within Unity. I ended up building a custom extension that allows for it, so I figured I'd share that here for anyone else who is searching for this stuff: https://github.com/LMNRY/SetProperty/
     
  40. RC-1290

    RC-1290

    Joined:
    Jul 2, 2012
    Posts:
    639
    Nice that you shared your code. I'm sure a lot of people will be looking for it :).
    (It's a bit off-topic though. You're not talking about the serialization part, but the automatic display in the inspector of properties. )

    In SetPropertyDrawer.cs you reset the value of GUI.changed manually. Are you aware of the existence of BeginChangeCheck and EndChangeCheck?

    There is another work-around that doesn't need Reflection, and it requires almost no extra code. I think I first saw it in a video about editor scripting, but it might be gone now, along with most of the rest of video.unity3d.com

    The trick is to mark a private property to be serialized so that it shows up in the inspector, and then use the OnValidate message method to run the value through the property. So for example, this property limits the value to be below 2:
    Code (csharp):
    1. [SerializeField] private float m_DisplayInInspector = 2.0f;
    2. public float ExampleProperty{
    3.     get { return m_DisplayInInspector; }
    4.     set{
    5.         m_DisplayInInspector = Mathf.Clamp(value, 0.0f, 2.0f);
    6.     }
    7. }
    8. /// <summary>
    9. /// Automatically called every time a value is updated
    10. /// </summary>
    11. protected void OnValidate(){
    12.     ExampleProperty = m_DisplayInInspector;// Feed value through property for validation
    13. }
    Unity then simply uses its standard Reflection magic to display and edit the value, while the code is still properly encapsulated.
    Of course, it relies on the OnValidate message from Monobehaviour, so you can't use it for assets :( (ScriptableObjects).
     
  41. amirebrahimi

    amirebrahimi

    Joined:
    Jan 27, 2008
    Posts:
    171
    Thanks. I wasn't aware of these functions. I'll try using those

    I'm aware of OnValidate and in the past it worked well. I specifically wanted to address the issue of ScriptableObjects and nested fields / classes elegantly. We have vanilla data classes that we mark serializable and that we want to be able to include as fields in ScriptableObjects. We edit them in Unity, but we also want to be able to set the same data classes via code. The nice part is that we define these attributes once and wherever the data class shows up its properties will get set correctly.
     
    Last edited: Apr 18, 2014
  42. WJ

    WJ

    Joined:
    Oct 25, 2012
    Posts:
    97
    Hmm these examples dont work, they just throw o->TestHideFlag (Object::kDontSave) && (options & kAllowDontSaveObjectsToBePersistent) == 0 and the asset is unusable.

    I guess the only way for polymorphic support is to create a separate object for each class in the hierarchy, this serializer is seriously lacking lol.
     
  43. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,221
    https://twitter.com/stramit/status/326724595786866688
     
  44. WJ

    WJ

    Joined:
    Oct 25, 2012
    Posts:
    97
    Watched it, didn't really solve my issue, I pretty much ended up writing a binary serializer that could take a SO Hierarchy and serialize it maintaining references, storing the data within the root SO and also allowing you to write a custom processor to handle any type of object you could concieve of using [SerializeProcessor(typeof(Color))].

    At run time it caches the first instance of the SO Tree and allows for fast cloning of the entire tree for unique instances.

    I had to waste a week on this only because:

    1- We cant use inheritance in lists without creating and attaching a unique SO for each type! In my behaviour tree thats a lot of types living in one list...

    2- Instantiate might return a unique instance for the root but it's so broken, it doesn't know about the SO tree so all the nodes end up being shared. (Is it really that hard to check if an object living in a list is referencing another SO and there for create a new instance of it during deserialization? In the video it was stated the the engine knows about all SO's so they must be easy to compare id's/references or even have it be optional using an attribute at the expense of a little performance.)

    For one of OO's most basic requirements to not be handled correctly wastes peoples time over and over for every new project and every complex plugin we wish to sell on the asset store.

    Unity should either support the hierarchy correctly or directly support polymorphism in lists at the very least...
     
    Last edited: Jun 2, 2014
    labandunga likes this.
  45. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    You could have saved yourself a week by buying "Full Inspector" off the Asset Store, which support binary serialization.
     
  46. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,614
    Yeah, but life is more complex when you're looking to solve a part of a larger thing that you want to sell. You'd have to enter some kind of special agreement with the owner or have a pre-requisite for your asset or... ugh.
     
  47. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    So, in this thread here about the new "Serialization depth warning" message in Unity: http://forum.unity3d.com/threads/4-5-serialization-depth.248321/

    It is stated that private fields are serialized by default, and you can only stop them from being serialized by specifically putting [NonSerialized] on them. This runs counter to the original post here, saying that private fields are not serialized by default and you have to specifically put [Serializable] on them if you want them. Can we see some sort of explanation of this contradiction? Right now it seems like what is happening is that it actually does serialize all private fields but just doesn't deserialize them. That seems to be the worst of both worlds... the extra cost and depth-max problems without any actual benefit of serialization. The current advice from Unity is that we should add "Nonserialized" to every single private or protected field. Why exactly? What is the case where you would actually want serialization but no deserialization? If that case doesn't exist, which it would seem to since Unity suggests adding the attribute to all private variables, then can't this be done automatically?
     
  48. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Makeshiftwings... You got it wrong.

    Private field are not serialized into data (ex.: saved on disk). However, for some reason still unknown, the editor serialize them when a serialization context reload occurs (ex.: script changes).

    While we are still waiting for some kind of explanation by Unity, my guess is they did that to keep the runtime editor data stable between reload, but forgot that everybody already placed security for that by assuming that data in private field are not kept properly. Well, dunno for everybody, but I did.

    It's an editor-only issue.
     
  49. r618

    r618

    Joined:
    Jan 19, 2009
    Posts:
    1,302
    Well, at least one reason for private fields being serialized is you can actually see them in Inspector in Debug view ( the context menu option of Inspector tab ); but it might be, as u said, only some ( in this case neat ) side effect of something else
     
  50. WJ

    WJ

    Joined:
    Oct 25, 2012
    Posts:
    97
    First I don't need all those features, not to say I wouldn't be happy to use them but eventually this engine will end up on the asset store, so yeah I do plan on including a custom inspector framework to handle all my graph nodes/components as part of the engine, but only for stuff thats part of my engine so as not to stop people from using whatever they like for their own code/components.

    I have tried lots of plug-ins and wasted lots of money, even just to see what others have done, nothing I tried was faster than my serializer or used up less space than mine either, and pretty much none of them could handle a complex graph with lots of references correctly and almost none could reference UnityEngine.Object as part of ser/des process using an indexing system, close to all didn't allow you to easily plug in your own processor, mine handles all basic types and built in types for you but if you have something custom you want to handle in a special way simply do something like this:

    Code (CSharp):
    1.     [SerializeProcessor(typeof(Enum))]
    2.     public class EnumTypeProcessor : SerializeHandler
    3.     {
    4.         #region implemented abstract members of SerializeHandler
    5.  
    6.         public override void SerializeField(ISerializedField field, ISerializedObject parent)
    7.         {
    8.             var value = field.GetValue(parent.Value);
    9.             parent.AddField(new SerializedField(field, Enum.GetName(field.FieldType, value)));
    10.         }
    Like angry said, I plan to sell the engine in the future, right now its been battle tested over 2 commercial games but theres more I want to do with it, and increasing performance/simplifying serialization process is one of those. It would be great if almost every plugin or game I build didn't need all these bespoke work-arounds to solve a fundamental requirement for clean well structured code.

    This is a nice article btw on custom serialization:
    http://codebetter.com/gregyoung/2008/08/24/fast-serialization/
     
    Assassinbeast likes this.