Search Unity

Odd serialization behaviour of UnityEvent inside an Editor Window

Discussion in 'Scripting' started by Freaking-Pingo, Nov 21, 2017.

  1. Freaking-Pingo

    Freaking-Pingo

    Joined:
    Aug 1, 2012
    Posts:
    310
    Hi Uniteers,

    I am observing a strange serialization behaviour for UnityEvents that I can't explain.

    Unity Events don't update their changes between the Editor Window and the Inspector unless you modify the invoking method. I have produced a .gif to illustrate it.

    Take note that:
    1. It works fine with bools
    2. It works fine with Game Objects
    3. Regardless what you do, the UnityEvent won't update between the Editor Window and the Inspector unless you change the invoking method. This only works if you change the invoking method in the Editor Window. This won't work when you do it in the Inspector


    Additional notes:
    • Upon recompiling, both the Editor Window and the Inspector will update to the same content
    • I did a previous attempt where I created a SerializableObject of the ExampleClass and created a SerializedProperty of the UnityEvent and drew it EditorGUILayout.PropertyField(unityEvent), but same problem appeared
    • I am using Unity 2017.2.0.f3

    The following code is what is being executed

    Code (CSharp):
    1. public class ExampleClass : MonoBehaviour {
    2.  
    3.     [SerializeField] bool exampleBool;
    4.     [SerializeField] GameObject exampleGameObject;
    5.     [SerializeField] UnityEvent examplEvent;
    6. }
    7.  
    Code (CSharp):
    1. public class ExampleEditor : EditorWindow
    2. {
    3.  
    4.     [MenuItem("Example/Debug GUI %g")]
    5.     static void Init()
    6.     {
    7.         ExampleEditor window = (ExampleEditor)EditorWindow.GetWindow(typeof(ExampleEditor));
    8.         window.Show();
    9.     }
    10.  
    11.     public void OnGUI()
    12.     {
    13.         ExampleClass exampleClass = GameObject.Find("Example").GetComponent<ExampleClass>();
    14.         Editor edi = Editor.CreateEditor(exampleClass);
    15.         edi.DrawDefaultInspector();
    16.     }
    17. }
     
    gibberingmouther likes this.
  2. Freaking-Pingo

    Freaking-Pingo

    Joined:
    Aug 1, 2012
    Posts:
    310
    I am not sure if this is linked to the same problem above but...

    Through more testing, I am also seeing something similar when it comes to serializing Animation Curves using similar approach. However, here the Editor Window does not seem to be able to update the animation curve, but it do work when using the inspector.

     
  3. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    UnityEvents don't apply modified changes until it you supply a valid invocation. since the invocation isn't valid there's no point to save that data. however, it will get saved when the editor is unloaded. When you do supply a valid invocation the root object gets flagged as dirty which causes all builtin editors drawing for that object to refresh.

    by default most editor windows only redraw when focused and in reaction to input events. They might also refresh if the object they represent was dirtied. Some constantly Repaint at around 10 fps, very few (like the scene view) repaint more than that.

    Thats because your editor code doesn't call serializedObject.Update. so any changes done outside the editor won't get seen until that editor is reloaded.

    likewise, your editor code is missing the typical changecheck and ApplyModifiedProperties thats common in most editor scripts. DrawDefaultInspector appears to handle most propertyfields for you in automatically applying any changes but the animationcurve seems to be a special case (since it opens a window) that isn't specifically handled in DrawDefaultInspector. The Inspector Editor window does handle a ton more things than simply calling DrawDefaultInspector.
     
  4. Freaking-Pingo

    Freaking-Pingo

    Joined:
    Aug 1, 2012
    Posts:
    310
    I have previously went down that route, but I am still experiencing the same issue. I have updated my code to the following and replaced the DrawDefaultInspector with a EditorGUILayout.PropertyField.


    Code (CSharp):
    1.     public void OnGUI()
    2.     {
    3.         ExampleClass exampleClass = GameObject.Find("Example").GetComponent<ExampleClass>();
    4.         SerializedObject serializedObject = new SerializedObject(exampleClass);
    5.         serializedObject.Update();
    6.  
    7.         SerializedProperty exampleEventProperty = serializedObject.FindProperty("exampleEvent");
    8.         EditorGUILayout.PropertyField(exampleEventProperty, true);
    9.  
    10.         //serializedObject.Update(); or should it be placed here? Regardless it is the same result
    11.  
    12.         if(GUI.changed)
    13.         {
    14.             serializedObject.ApplyModifiedProperties();
    15.         }
    16.     }
    I am still experiencing the same behaviour.
     
  5. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    916
    What SerializableObjects are is nothing more than a proxy object that has the serialized data that was in a instance of a class. Its like a photo of a car. The photo itself isn't actually a car, its just a snapshot representation of it. to take this metaphor further, that photo would also show any visible state of that car at the time the photo was taken. so if the car was red when the photo was taken the photo will show a red car. and the photo will continue to show a red car even if the car got a new paint job for a blue color. you'd have to take a new photo before it'd show up as blue in the photo.

    SerializedObjects are the same. When you create a new SerializedObject with a reference, the serialized object will have a snapshot of the referenced object's state at the time it was created. SerializedObject.Update() simply tells the serializedObject to retake a new snapshot of the object its already representing for. thus using Update is basically pointless immediately after creating a new serializedObject (since Update is basically just like new SerializedObject, just without the allocation). SerializedObjects are typically created in OnEnable not OnGUI, not for proper behavior, but more as a performance thing (as creating new objects every OnGui is heavy on the memory allocation side).

    as per your Comment, you want to call Update before you try to draw any properties so that whats drawn to screen is up-to-date (so its typically one of the first things called in your OnGUIs).

    I believe GUI.changed only applies if you use the GUI class, as such I don't think ApplyModifiedProperties is actually getting called. if you're using EditorGUI and EditorGUILayout, you need to use EditorGUI.BeginChangeCheck and EditorGUI.EndChangeCheck.
     
  6. Freaking-Pingo

    Freaking-Pingo

    Joined:
    Aug 1, 2012
    Posts:
    310
    Regardless, wrapping ApplyModifiedProperties inside an EditorGUI.EndChangeCheck() if statement or not, I am not seeing the desired behaviour. Editor still behaves like the first .gif I provided

    Code (CSharp):
    1.     public void OnGUI()
    2.     {
    3.         ExampleClass exampleClass = GameObject.Find("Example").GetComponent<ExampleClass>();
    4.         SerializedObject serializedObject = new SerializedObject(exampleClass);
    5.  
    6.         SerializedProperty exampleEventProperty = serializedObject.FindProperty("exampleEvent");
    7.  
    8.         EditorGUI.BeginChangeCheck();
    9.         EditorGUILayout.PropertyField(exampleEventProperty, true);
    10.      
    11.         if(EditorGUI.EndChangeCheck())
    12.         {
    13.             serializedObject.ApplyModifiedProperties();
    14.         }
    15.  
    16.         //serializedObject.ApplyModifiedProperties(); Running this outside the EndChangeCheck() doesn't change anything
    17.     }
     
  7. Freaking-Pingo

    Freaking-Pingo

    Joined:
    Aug 1, 2012
    Posts:
    310
    I can't figure out whether my approach is wrong or whether there is a bug luring somewhere. Could anyone post a functional example of serializing an UnityEvent in an Editor Window?
     
  8. genrysss

    genrysss

    Joined:
    Mar 26, 2018
    Posts:
    21
    @Freaking-Pingo, Hi. Did you figure out how serialize UnityEvent in Editor Window?
     
  9. tcmeric

    tcmeric

    Joined:
    Dec 21, 2016
    Posts:
    190
    Ping to this in 2019. Still havent found a solution :/
     
  10. Freaking-Pingo

    Freaking-Pingo

    Joined:
    Aug 1, 2012
    Posts:
    310
    Sorry, I wish I could help but I can't recall whether I actually found a solution to this or not.
     
  11. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,279
    Feel free to file a bug report so we can investigate.