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. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    I don't mean saved on disk, I mean serialized back and forth into C++ land when playing/stopping like the first post mentions. It doesn't make any sense to have a weird "in-between" state between [Serializable] and [NonSerialized] by default, where it serializes the data but doesn't actually deserialize it correctly. It sounds like a bug.

    Right, but it still "requires" that you add [NonSerialized] to potentially thousands of variables throughout your code just to get around this bug (feature?) which is a waste of time and makes code uglier for no apparent reason.
     
  2. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Not thousands... Usually only variables that would offer a dependency loop entry point. In a project, I had to add it to 3 fields to remove 7000 errors. For other variable, the serialization time gained is so small I doubt it's worth the time to flag them.
     
  3. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    But my main question is, is there any reason not to add [NonSerialized] to a private field? Since Unity is incapable of actually keeping track of the private field across reloads unless you add [Serializable] (as per the original post in this thread), it seems pointless to have an "in-between" state by default where it gets uselessly serialized for no apparent reason.
     
  4. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    No reason not to... Maybe just time.

    Oh, for me it is totally useless to have an "in-between" state as I always assumed one or the other and always coded with that assumption in mind. From what I heard, this "in-between" feature have been there for all version 4 and maybe even 3 without anybody noticing it. When I wanted a private field to exist between reload, I would manually flag "SerializedField", and would assume not-flagged one to be unusable garbage.

    Like I said, still waiting for Unity to get their heads out of their asses and start doing some explaining. I would expect that maybe their internal editor classes are built around that behaviour and they never cared that not documenting it would mean people banging their heads on some walls and waste untold amount of time on serialization reload because the editor would start spinning on itself for minutes.
     
    r618 likes this.
  5. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Yeah I think we're on the same page. I think if there's no reason not to put [NonSerialized] on all private fields than Unity should just handle that automatically for us. It's especially weird this late in Unity's life for the developers to come out and say "Well, duh, of course you should have been putting [NonSerialized] on all your private fields to avoid serializing them" when it's pretty clear no one knew we were supposed to be doing that until now. And the original post here makes me think Unity themselves weren't really clear on how their own serialization works (and maybe still aren't).

    Bottom line - I don't want to start putting [NonSerialized]'s anywhere if Unity is just going to fix this in the next update. It's annoying to already see assets on the asset store being updated to add that attribute everywhere if it's really just a bug that will be fixed soon.
     
  6. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    I know, I'm not updating anything on the store until I get a final word from Unity.

    Frankly, Unity's serialization is just about as bad as it get. I would expect that after so many years of existence, stuff like struct, interface, dictionary, polymorphism and object reference would be a given! I mean, they could use .NET binary serialization and it would do all that out of the box. I sometimes have that weird feeling that it was first made by some novice we never did this kind of thing before and they just built on top of it. I have hope they would redo it from scratch in Unity 5.
     
  7. AlkisFortuneFish

    AlkisFortuneFish

    Joined:
    Apr 26, 2013
    Posts:
    970
    For what it's worth, structs are now supported, as of 4.5

    Edit: Typoing "not" instead of "now" made this the world's most useless post... ;-)
     
  8. bakedlama

    bakedlama

    Joined:
    Nov 13, 2013
    Posts:
    1
    LightStriker, thanks for recommending FullInspector. I've just imported the free trial and gotten it working in my first ever serious Unity project. I'd run into the new error in 4.5 and followed your thread about it, eventually finding my way here. One of the mods mentioned the new serialization events in that other thread and I had started looking into how to use that, but FullInspector just takes care of everything and will let me get back to writing real features for my game. I'd also originally designed some stuff using abstract classes and had to change that around to work in the stock inspector, but once again FullInspector 'just works' with abstract classes. I may have found an issue with duplicating array items of those abstract types but I'll keep digging into the free trial and file any bugs I come across.

    I also checked out your Advanced Inspector plug-in, but am I correct in thinking that it doesn't take care of serialization the way FullInspector does?

    When I read that .NET has serialization built in I was as confused as you are as to why Unity has its own system with these bizarre and arbitrary limitations. I'm still new to this so I'm not entirely sure how it works, though, so I'm in no position to be angry about it. It seems that if there's one thing Unity does really well it's extensibility, so right now I'm just happy that I've found a plug-in that takes care of this for me.
     
  9. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Yeah, FullInspector exposes one of those things that should have been there from the start in Unity; support for alternative serialization.

    You're right, AdvancedInspector is not the same thing, and does not work at all on the same ground. FI solves a serialization issue, AI solves a interface/user interaction issue (Ex.: Like copy/pasting values). There's a few features both package offer - I think - like exposing properties, but there's far far more features of both package that don't overlap than features that do.

    AdvancedInspector's goal is to uncouple the Inspector from serialization. Usually, the Inspector only display item Unity can and will save. The AI is not limited by that at all, and aim to offer all the control over how the Inspector looks or behave without the need of any custom editor. You can see from the video;


    Now... Can both FI and AI work with each other? I truly have no idea. I have the feeling they can, as I think have some people using AI that appear to be using FI too.

    You and me both. From my point of view, Unity was made by people who never did that before, and which over 4 version of the software, became better and more experienced up to the good software we have now, but got cornered into backward compatibility with some very stupid original moves. I really hope that for Unity 5 they would dump ALL their very retarded serialization and use a better solution, on par with the rest of the world best serialization algo. The guy behind FI also showed that Unity's serialization is quite slow compared to other serialization.
     
    IvanAuda likes this.
  10. Grhyll

    Grhyll

    Joined:
    Oct 15, 2012
    Posts:
    119
    Hi,

    First of all, thanks for this very, very useful guide. It has helped me to do some quite cool stuff, but now I'm stuck, and maybe someone here will be able to help me...
    I'm trying to save some data in an asset file, and the problem comes when I try to save a list of scriptable objects in a scriptable objects. I have reproduced it with a simple exemple inspired by the first post.



    So here's my scriptable object:

    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEditor;
    6.  
    7.  
    8. [Serializable]
    9. public class TheObject : ScriptableObject
    10. {
    11.    [SerializeField]
    12.    private List<TheSubobject> m_Instances;
    13.    
    14.    public void OnEnable ()
    15.    {
    16.      Debug.Log("In OnEnable of TheObject");
    17.      if (m_Instances == null)
    18.        m_Instances = new List<TheSubobject>();
    19.      
    20.      //hideFlags = HideFlags.HideAndDontSave;
    21.    }
    22.    
    23.    public void OnGUI ()
    24.    {
    25.      foreach (var instance in m_Instances)
    26.        instance.OnGUI ();
    27.      
    28.      if (GUILayout.Button ("Add Base"))
    29.        m_Instances.Add(CreateInstance<TheSubobject>());
    30.    }
    31. }
    32.  

    It's custom inspector (I have let the OnGUI function in the object because it was like this in the tuto, although the tuto did not use a custom inspector, which I need):

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4. using System.Collections;
    5.  
    6. [CustomEditor(typeof(TheObject))]
    7. public class TheObjectInspector : Editor
    8. {
    9.    [MenuItem("Test/Prompt")]
    10.    public static void Prompt()
    11.    {
    12.      string assetPath = "Assets/Foo.asset";
    13.      
    14.      if(AssetDatabase.LoadAssetAtPath(assetPath, typeof(TheObject)) == null)
    15.      {
    16.        TheObject asset = (TheObject) ScriptableObject.CreateInstance<TheObject>();
    17.        AssetDatabase.CreateAsset(asset, assetPath);
    18.        AssetDatabase.SaveAssets();
    19.      }
    20.      Selection.activeObject = AssetDatabase.LoadAssetAtPath(assetPath, typeof(TheObject));
    21.      
    22.    }
    23.  
    24.    TheObject theObject;
    25.    SerializedObject serializedTheObject;
    26.    
    27.    void OnEnable()
    28.    {
    29.      Init();
    30.    }
    31.    
    32.    void Init()
    33.    {
    34.      theObject = (TheObject) target;
    35.      serializedTheObject = new SerializedObject(theObject);
    36.    }
    37.  
    38.    public override void OnInspectorGUI()
    39.    {
    40.      if(theObject == null)
    41.        Init();
    42.      
    43.      EditorGUI.BeginChangeCheck ();
    44.      
    45.      EditorGUILayout.Space();
    46.      
    47.      theObject.OnGUI();
    48.      
    49.      if(EditorGUI.EndChangeCheck())
    50.      {
    51.        Debug.Log("Saving BehavioursCollection changes.");
    52.        
    53.        serializedTheObject.ApplyModifiedProperties();
    54.        
    55.        EditorUtility.SetDirty(theObject);
    56.        AssetDatabase.SaveAssets();
    57.      }
    58.    }
    59. }
    60.  

    And finally the class used in this list:

    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEditor;
    6.  
    7.  
    8. [Serializable]
    9. public class TheSubobject : ScriptableObject
    10. {
    11.    [SerializeField]
    12.    protected int m_IntField;
    13.  
    14.    public void OnEnable() { hideFlags = HideFlags.HideAndDontSave; }
    15.    
    16.    public virtual void OnGUI ()
    17.    {
    18.      m_IntField = EditorGUILayout.IntSlider("IntField", m_IntField, 0, 10);
    19.    }
    20. }
    21.  
    While in the editor, everything is great, but as soon as I close and reopen Unity, when I click on the asset I get

    Code (csharp):
    1.  
    2. NullReferenceException: Object reference not set to an instance of an object
    3. TheObject.OnGUI () (at Assets/TheObject.cs:26)
    It seems that TheObject remembers he had for example 3 items in its list, but those items are null... Any idea?
     
  11. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    A ScriptableObject is a file.

    You cannot save a file inside another file. Is there a reason why "TheSubObject" should be a separated file?
     
  12. Grhyll

    Grhyll

    Joined:
    Oct 15, 2012
    Posts:
    119
    I thought since TheSubObject is serializable, it would be saved inside TheObject file. I have tried indeed to remove this inheritance, and in this case it works, but my problem is that in my concrete situation I store subclasses of TheSubObject in the list of TheObject, and it is mentionned in the first post of this topic that in this case I need to use ScriptableObjects, or the attributes of the child class won't be saved.

    So I have changed it a bit to precise my issue; I have removed the inheritance from TheSubobject to ScriptableObject to have this in TheObject:


    Code (csharp):
    1.  
    2. public void OnGUI ()
    3.    {
    4.      foreach (var instance in m_Instances)
    5.        instance.OnGUI ();
    6.    
    7.      if (GUILayout.Button ("Add Base"))
    8.        m_Instances.Add(new TheSubobject());
    9.      if (GUILayout.Button ("Add Child"))
    10.        m_Instances.Add(new TheSubObjectChild());
    11.    }
    12.  
    And added a new child class:

    Code (csharp):
    1.  
    2. [Serializable]
    3. public class TheSubobject
    4. {
    5.    [SerializeField]
    6.    protected int m_IntField;
    7.  
    8.    public virtual void OnGUI ()
    9.    {
    10.      m_IntField = EditorGUILayout.IntSlider("IntField", m_IntField, 0, 10);
    11.    }
    12. }
    13.  
    14. [Serializable]
    15. public class TheSubObjectChild : TheSubobject
    16. {
    17.    [SerializeField]
    18.    protected float m_FloatField;
    19.  
    20.    public override void OnGUI ()
    21.    {
    22.      base.OnGUI();
    23.      m_FloatField = EditorGUILayout.FloatField(m_FloatField);
    24.    }
    25. }
    26.  
    Now it is correctly saved when restarting Unity, but as warned in the first post I loose the child type when doing so.


    So I guess, from your previous answer, that a solution could be to save each element of the list in a new asset file. It does not seem very clean, but at least it could work.
     
    Last edited: Jul 17, 2014
  13. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    ScriptableObject is NOT about being serializable, it's about being able to save it to a file.

    If you want a class to be serializable, there's the [Serializable] attribute.

    Yes, there is no way to do polymorphism in sub-object in Unity. If you want polymorphism, you will have to save each object in its separated file.
     
  14. Grhyll

    Grhyll

    Joined:
    Oct 15, 2012
    Posts:
    119
    Yes, when I said "TheSubObject is serializable" I was speaking about the [Serializable]. Thank you for your help anyway, I understand things a little better now! I'll try to look into subassets things to see if I could save those assets in the same file as the main one.
     
  15. Grhyll

    Grhyll

    Joined:
    Oct 15, 2012
    Posts:
    119
    So just in case someone would want to use ScriptableObjects with polymorphism, a little trick that I spent some hours finding: put each of your child class in its own separate file! If you put the mother and child class in the same file, it just won't work when trying to reload your array of elements.
    Here is a little functionnal exemple:


    Code (csharp):
    1. SObject.cs:
    2.  
    3. using System;
    4. using System.Collections.Generic;
    5. using UnityEngine;
    6. using UnityEditor;
    7.  
    8.  
    9. [Serializable]
    10. public class SObject : ScriptableObject
    11. {
    12.    public static string assetPath = "Assets/Resources/Bar.asset";
    13.    public static string dataAssetPath = "Assets/Resources/DataBar.asset";
    14.    
    15.    [SerializeField]
    16.    private List<Subobject> m_Instances;
    17.    [SerializeField]
    18.    private ScriptableObject data;
    19.  
    20.    public List<Subobject> list
    21.    {
    22.      get { return m_Instances; }
    23.    }
    24.    
    25.    public void OnEnable ()
    26.    {
    27.      if (m_Instances == null)
    28.      {
    29.        m_Instances = new List<Subobject>();
    30.        data = (ScriptableObject) AssetDatabase.LoadAssetAtPath(dataAssetPath, typeof(ScriptableObject));
    31.      }
    32.    }
    33.    
    34.    public void OnGUI ()
    35.    {
    36.      if(m_Instances == null)
    37.      {
    38.        return;
    39.      }
    40.      
    41.      foreach (var instance in m_Instances)
    42.      {
    43.        instance.OnGUI ();
    44.      }
    45.  
    46.      if (GUILayout.Button ("Add Base"))
    47.      {
    48.        Subobject newObject = (Subobject) ScriptableObject.CreateInstance<Subobject>();
    49.        AssetDatabase.AddObjectToAsset(newObject, data);
    50.        m_Instances.Add(newObject);
    51.      }
    52.      if (GUILayout.Button ("Add Child"))
    53.      {
    54.        SubobjectChild newObject = (SubobjectChild) ScriptableObject.CreateInstance<SubobjectChild>();
    55.        AssetDatabase.AddObjectToAsset(newObject, data);
    56.        m_Instances.Add(newObject);
    57.  
    58.      }
    59.    }
    60. }

    Code (csharp):
    1. SObjectInspector.cs:
    2.  
    3. using UnityEngine;
    4. using UnityEditor;
    5. using System.Collections;
    6.  
    7. [CustomEditor(typeof(SObject))]
    8. public class SObjectInspector : Editor
    9. {
    10.    [MenuItem("Test/Prompt2")]
    11.    public static void Prompt2()
    12.    {
    13.      if(AssetDatabase.LoadAssetAtPath(SObject.assetPath, typeof(SObject)) == null)
    14.      {
    15.        ScriptableObject data = (ScriptableObject) ScriptableObject.CreateInstance<ScriptableObject>();
    16.        AssetDatabase.CreateAsset(data, SObject.dataAssetPath);
    17.  
    18.        SObject asset = (SObject) ScriptableObject.CreateInstance<SObject>();
    19.        AssetDatabase.CreateAsset(asset, SObject.assetPath);
    20.        AssetDatabase.SaveAssets();
    21.      }
    22.      Selection.activeObject = AssetDatabase.LoadAssetAtPath(SObject.assetPath, typeof(SObject));
    23.      
    24.    }
    25.    
    26.    SObject theObject;
    27.    
    28.    void OnEnable()
    29.    {
    30.      Init();
    31.    }
    32.    
    33.    void Init()
    34.    {
    35.      theObject = (SObject) target;
    36.    }
    37.    
    38.    public override void OnInspectorGUI()
    39.    {
    40.      if(theObject == null)
    41.        Init();
    42.      
    43.      EditorGUI.BeginChangeCheck ();
    44.      
    45.      EditorGUILayout.Space();
    46.      
    47.      theObject.OnGUI();
    48.      
    49.      if(EditorGUI.EndChangeCheck())
    50.      {
    51.        EditorUtility.SetDirty(theObject);
    52.        
    53.        foreach(Subobject o in theObject.list)
    54.        {
    55.          EditorUtility.SetDirty(o);
    56.        }
    57.        
    58.        AssetDatabase.SaveAssets();
    59.      }
    60.    }
    61. }

    Code (csharp):
    1. Subobject.cs:
    2.  
    3. using System;
    4. using System.Collections.Generic;
    5. using UnityEngine;
    6. using UnityEditor;
    7.  
    8.  
    9. [Serializable]
    10. public class Subobject : ScriptableObject
    11. {
    12.    [SerializeField]
    13.    protected int m_IntField;
    14.    
    15.    public virtual void OnGUI ()
    16.    {
    17.      m_IntField = EditorGUILayout.IntSlider("IntField", m_IntField, 0, 10);
    18.    }
    19. }

    Code (csharp):
    1. SubobjectChild.cs
    2.  
    3. using System;
    4. using System.Collections.Generic;
    5. using UnityEngine;
    6. using UnityEditor;
    7.  
    8. [Serializable]
    9. public class SubobjectChild : Subobject
    10. {
    11.    [SerializeField]
    12.    protected float m_FloatField;
    13.    
    14.    public override void OnGUI ()
    15.    {
    16.      base.OnGUI();
    17.      m_FloatField = EditorGUILayout.FloatField(m_FloatField);
    18.    }
    19. }

    So nothing really fancy, essentially the fact that the child classes MUST have their own file, otherwise SObject will have trouble reloading its array when restarting Unity.
     
    Medding3000 likes this.
  16. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    o_O
     
  17. Grhyll

    Grhyll

    Joined:
    Oct 15, 2012
    Posts:
    119
    Arf v_v I guess it's thanks to this that I had finally tried to do this, I had read it too quickly without really understanding it... It would have saved me some hours to read it slowly and twice! Thanks again for the help.
     
  18. Medding3000

    Medding3000

    Joined:
    Dec 11, 2013
    Posts:
    45
    This has cost me HOURS of crawling the whole web.
    Im gonna say it again in capitals, bold, and huge size, just for everybody that comes here.
    Clearly a bug. Any unity dev that wants to reply to this issue?

    DERIVED CLASSES NEED A SEPERATE FILE TO SERIALIZE PROPERLY
     
    forcepusher likes this.
  19. Joviex

    Joviex

    Joined:
    Jan 23, 2011
    Posts:
    44
    And the dead rose up from the forum ground....

    First, excellent information. As for a question? Aye.

    Did the other bullet points ever get posted? I couldnt find anything under your account.

    Edit:

    http://blogs.unity3d.com/2012/10/25/unity-serialization/

    Sound about the full coverage? I just don't see any explicit mention of saving serialized data direct as an asset file.

    Cheers.
     
    Last edited: Aug 13, 2014
  20. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    I have a question regarding serialization - when defining new property drawers, i get a reference to the serialized component (SerializedProperty) to work with.

    Is there any API that allowes me to take a component and run the same serialization that unity is using to create a SerializedProperty ?
     
  21. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Not sure what you mean by API here, but you can do;

    http://docs.unity3d.com/ScriptReference/SerializedObject.html

    Code (CSharp):
    1.             SerializedObject so = new SerializedObject(MyUnityObject);
    2.             SerializedProperty sp = so.FindProperty("MyProperty");
     
  22. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    Thanks ! that was what i was missing :)

    Is there any runtime APIs for dealing with serialization?

    SerializedProperty and SerializedObject are both editor classes. Is there any way of serializing/deserializing these during runtime ? (i know i can write my own serialization code, i am wondering whether there's any API Unity provides to perform these at runtime so i don't have to re-invent the wheel)
     
  23. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Nope, nothing at runtime. For some reason, Unity believes their S***ty serialization is such a special snowflake that it should stay hidden away in a black box - the editor.
     
    IQpierce likes this.
  24. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    Let's say i have a reference to a SerializedProperty (or multiple of these), isn't there any built in option to use those to modify an object at runtime ?
     
  25. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    You can modify the loaded object using reflection, but you won't be able to re-save it.
     
  26. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    Bummerz..

    Is there any easy way to serialize a SerializedObject and SerializedProperty while in the editor ?

    Those classes are not serialized as far as i can tell. How can i save an asset that holds a reference to one of these?
     
  27. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
  28. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
  29. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    You can't. Why would you need that exactly, since you cannot save a Unity Object are runtime?

    You should start by explaining what you want to do, not how you expect to do it.
     
  30. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    Well, suppose i wanted to build my own Undo system, since the one provided in the editor doesn't work for all my needs.
    I'd like to be able to serialize the inspector state and to rollback (undo).

    A generic way to do that is to use the serialization classes (e.g: SerializedObject and SerializedProperty) since that way i can write generic code just as Unity is treating those classes when it serializes them, instead of having to write a specific implementation for each class.

    An option i thought of was "cloning" the original component but that is way too messy (since it clones the whole game object and not only the component).
     
  31. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    What is your need?
     
  32. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    There are different things that i wish i could implement, for example - a generic copy/paste context menu for inspectors.

    Currently it is available at the component level. Let's say my component holds a public field of some other complex, serializable type.

    I would like to copy all of the instance's serialized properties from one instance to another via copy-paste (e.g: right click in the inspector, select Copy, and paste that into another instance).

    One way to implement that would've been to iterate over the inspector's values via SerializedObject and SerializedProperty copy those to some buffer that could be serialized itself.

    When pasting i would read that buffer and paste those values to the target object.

    The problem is withusing the Serialized* classes - they cannot be serialized (not marked with Serializable and not derived from Object).
     
  33. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Like this?



    or



    Good luck. It's not an easy task, and it wasn't done with SerializedObject or SerializedProperty. Instead, I travel the objects hierarchy using reflection, and assume everything deriving from UnityEngine.Object is a reference.
     
  34. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    Something similar yes.

    I have a public SomeObject[] Something;

    This object was edited in the inspector and i'd like to copy it to some other instance holding an array of this object.
    The problem is that you can only copy the entire component, but not a single field from it.

    If i could serialize a SerializedObject and store it somewhere, i could read all serialized fields of this class and copy them to the other object.

    Using reflection sounds more complicated than what i was after, but may also be a valid option (it is also probably generic since you can iterate over all fields of the criteria you want, e.g: public, instance, serializable, value/primitive types or derived from UnityEngine.Object).
     
  35. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Well, the screenshot I posted was with a contextual menu invoked while right-clicking on a field label. That is a copy/paste of a field. (You can check my signature for that asset; Advanced Inspector)

    I still think reflection would be your best bet. Like you said, it's generic and you have control over how it is handled. Sure, it could be quite some work, however I'm not sure you could get the result you want from SerializedObject. By experience, I found SerializedObject/SerializedProperty to be more indirect, troublesome and limited than they are worth. To be frank, I'm not sure I could copy one SerializedProperty to another of a different SerializedObject without some weird conversion in the process.

    Someone else might come up with a solution to that, but I don't have one using SO/SP.
     
  36. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    Well, SerializedObject has the CopyFromSerializedProperty method that applies a property to the SO instance. I suppose it does that by looking up the property path and copies the values into that, so that might work. But again, you cannot serialize the SO and SP, so there's no way i could keep a persistent copy of those values so once they're gone, you cannot use them anymore.

    I would take a look at Reflection (i'm already familiar with the APIs), i suppose you could achieve the same (if not even better) results using it over SO/SP.

    P.S i will check your asset "Advanced Inspector" to see if it's something we'd like to use :)
     
  37. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    http://docs.unity3d.com/ScriptReference/SerializedObject.CopyFromSerializedProperty.html

    "Copies a value from a SerializedProperty to the same serialized property on this serialized object."

    Yeah, I saw that... While I never used it, it makes me feel it only works if the two SerializedObject are the same, and you're copying the same SerializedProperty over. It doesn't look you like you can take any SP and paste it over another SP, even if the underlying values are the same.

    It's a SO method, and it takes only one SP parameter, so there's no way to tell it which SP to paste over.
     
  38. cristivp

    cristivp

    Joined:
    Nov 1, 2012
    Posts:
    6
    Do public fields in custom inspectors get serialized on an assembly reload? Or am I meant to use a editor window if I want to some custom data to get serialized?

    Cheers
     
    WiedemannD likes this.
  39. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    I would have to check, but I'm pretty sure they should be.
     
  40. Shiver

    Shiver

    Joined:
    Dec 20, 2013
    Posts:
    8
    I can't seem to get the EditorWindow example to save the values. I've simply copy pasted the code in the code block right before "Some Serialization Rules" into 3 different files MyWindow.cs, NestedClass and SerializeMe.

    From my understanding of the original post, this should allow me to modify the values, close the window and open it again with the same values correct?

    I've tried with basic types like int and float rather than the SerializeMe and still, I get nothing. Does anybody have an idea how to get this to work?

    Thanks!
     
  41. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Only if they are static. Otherwise the EditorWindow you open isn't the same as the one you closed. Unless you are doing some kind of object recycling.
     
  42. Shiver

    Shiver

    Joined:
    Dec 20, 2013
    Posts:
    8
    Thanks for that clarification. I think I might be looking at making custom assets for what I want to do.
     
  43. LovesSolvingProblems

    LovesSolvingProblems

    Joined:
    Jan 22, 2015
    Posts:
    17
    Amazing article. Thank you for sharing your knowledge.
     
  44. Tommy-Core

    Tommy-Core

    Joined:
    Jan 24, 2014
    Posts:
    21
    Thank you, Tim. Reading your article helped me solve a problem within minutes that I had fought with for hours. You're awesome =)
     
  45. IQpierce

    IQpierce

    Joined:
    Jan 24, 2011
    Posts:
    43
    "How to save your serialized data as an asset"
    I'm curious about this part - Did Tim ever address this? Might be really useful for something I'm working on.
     
  46. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    All types derived from UnityEngine.Object are serializable.

    In order to serialize data into an asset, you should:
    1. Create an instance of your data in a class derived from UnityEngine.Object
    2. Call AssetDatabase.CreateAsset(object, path) to save your object into an asset.
     
  47. WiedemannD

    WiedemannD

    Joined:
    Mar 4, 2015
    Posts:
    19

    Hi there,
    I'm basically stuck with the same question for quite a while now: having persistent references (serialized fields) in an EditorWindow works fine, but what about an Editor? I see both are inherited from ScriptableObject, I set the Editor's hideflags, I marked it as [System.Serializable] and just for the sake of it I also marked the public field that I hoped to persist between assembly reload as [SerializeField]. But a hit on Play just resets everything. Also I noticed that the Editor probably gets initialized multiple times, as OnEnable() triggers multiple times at least.

    I thought maybe Tim you would have an answer to this: What's the difference in serialization between Editor and EditorWindow and is there a way to have persistent references in a custom Editor?

    (By the way, thank you very much for the mega post and your presentation
    , it helped me a lot to understand Unity's serialization better)

    Cheers


    EDIT:
    Well after reading the last paragraph of this article I'm not sure it's possible with JUST an Editor. (http://wiki.unity3d.com/index.php/Editor_Scripting_Detailed_Overview)
    To me this sounds as I would need to store my persistent fields in the component itself (which wouldn't make much sense if they are about Editor only specifics), or elsewhere, e.g. an EditorWindow. EditorWindow won't be destroyed on assembly reload, but also correctly serialized/deserialized to keep refs persistent. Whereas Editor gets destroyed when the corresponding Component gets out of focus or on assembly reload, but serialization/deserialization does not seem to happen that way, so refs get lost.
     
    Last edited: May 8, 2015
  48. garrido86

    garrido86

    Joined:
    Dec 17, 2013
    Posts:
    233
    I made a class that inherits from EditorWindow and it paints on the Scene View some GUI Elements to manipulate Material Properties from the selected Gameobject. It works perfectly but since the Material properties aren't serialized, you can't undo changes. I honestly did run out of Ideas how to work that out.
     
  49. WiedemannD

    WiedemannD

    Joined:
    Mar 4, 2015
    Posts:
    19
    I think material properties must be serialized somehow as well, or they wouldn't be persistent, but you're right, changing them does not trigger any undo event (at least from what I can say, if I do it via the inspector).

    I know it's a bit tedious, but how about creating your own custom undo solution? If the interface is yours, you could just track all changes in your own custom undo stack.
     
  50. garrido86

    garrido86

    Joined:
    Dec 17, 2013
    Posts:
    233
    I have never implemented a undo solution or even know how this works, so this would definitely make a nice challenge for me. Do you have by any change some resources/tutorials I can look into?