Search Unity

Resolved How Do I CORRECTLY Retain Custom EditorWindow Data?

Discussion in 'UI Toolkit' started by FuguFirecracker, Jan 18, 2021.

  1. FuguFirecracker

    FuguFirecracker

    Joined:
    Sep 20, 2011
    Posts:
    419
    All assigned data vanishes when I maximize any other EditorWindow.

    I thought I had this solved in a more complex project -wherein I use a ScriptableObject backend to retain state - but the failure of this simple EditorWindow has my quite vexed.

    missingGameObects.gif

    And Here is the code:
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3. using UnityEditor.UIElements;
    4. using UnityEngine.UIElements;
    5.  
    6. namespace FuguFirecracker.Utilities
    7. {
    8.     public class MindTheCubes : EditorWindow
    9.     {
    10.        
    11.         private GameObject _goZero;
    12.         private GameObject _goOne;
    13.      
    14.         private  GameObject[] _gameObjects = new GameObject[2];
    15.  
    16.  
    17.         [MenuItem("Tools/Mind the Cubes")]
    18.         private static void ShowWindow()
    19.         {
    20.             var window = GetWindow<MindTheCubes>();
    21.             window.titleContent = new GUIContent("Mind the Cubes");
    22.             window.Show();
    23.         }
    24.  
    25.  
    26.         private void OnEnable()
    27.         {
    28.             var objectFieldZero = new ObjectField {objectType = typeof(GameObject), value = _goZero};
    29.             objectFieldZero.RegisterValueChangedCallback(evt => _goZero = (GameObject) evt.newValue);
    30.            
    31.             var objectFieldOne = new ObjectField {objectType = typeof(GameObject), value = _goOne};
    32.             objectFieldOne.RegisterValueChangedCallback(evt => _goOne = (GameObject) objectFieldOne.value);
    33.            
    34.             var objectFieldTwo = new ObjectField {objectType = typeof(GameObject), value = _gameObjects[0]};
    35.             objectFieldTwo.RegisterValueChangedCallback(evt => _gameObjects[0] = (GameObject) objectFieldTwo.value);
    36.            
    37.             var objectFieldThree = new ObjectField {objectType = typeof(GameObject), value = _gameObjects[1]};
    38.             objectFieldThree.RegisterValueChangedCallback(evt => _gameObjects[1] = (GameObject) evt.newValue);
    39.  
    40.  
    41.             rootVisualElement.Add(objectFieldZero);
    42.             rootVisualElement.Add(objectFieldOne);
    43.             rootVisualElement.Add(objectFieldTwo);
    44.             rootVisualElement.Add(objectFieldThree);
    45.  
    46.         }
    47.     }
    48. }
    Please direct me if I am doing something wrong. Or have I found a bug here? Surely this cannot be "known and expected" behaviour.
     
  2. oscarAbraham

    oscarAbraham

    Joined:
    Jan 7, 2013
    Posts:
    431
    If you put the
    [SerializeField]
    attribute before your GameObject fields, it should work.
     
  3. FuguFirecracker

    FuguFirecracker

    Joined:
    Sep 20, 2011
    Posts:
    419
    You'd think it would, but it doesn't; Nor does making the fields 'public'
    Already tried all permutations of accessors and attributes.
    God help ya if you make your collections 'readonly' ! lol

    Thanks for taking the time ..
     
  4. oscarAbraham

    oscarAbraham

    Joined:
    Jan 7, 2013
    Posts:
    431
    No problem. I'd guess then that either your window instance is being recreated for some reason, or your ui fields are setting the backing fields to null in OnEnable.

    A third option would be that your GameObjects are being recreated, in which case you can serialize their instance ids instead and get them with EditorUtility.InstanceIDToObject in OnEnable.

    If it's the first one, the solution you mentioned about using a ScriptableObject backend should work. You'd create a custom class for your SO backend, an in the window's OnEnable your search for your backend with FindObjectsOfType, if none is found, you instantiate a new one.
     
    Last edited: Jan 18, 2021
  5. FuguFirecracker

    FuguFirecracker

    Joined:
    Sep 20, 2011
    Posts:
    419
    I really do appreciate you spending time to consider this. Thank you.

    Some clarifications:

    Yeah, that's a likely culprit.... But it is not me doing it. The script I posted is the only script in the project with the sole function of illustrating this deleterious behaviour.

    That is not happening. There are no other scripts in the entirely of the project.

    Yes it does, I've done it. But that is a whole mess of boilerplate code to write to safeguard against a buggy EditorWindow implementation --on the chance that someone maximizes another EditorWindow and wipes out all their work.

    So the realities are ... I'm just not doing it right, or it is a miserable bug that requires a me to code-up a couple of hundred lines to safeguard against.
     
  6. oscarAbraham

    oscarAbraham

    Joined:
    Jan 7, 2013
    Posts:
    431
    It's no problem, really.
    Sometimes, scene GameObjects are recreated, but they keep their instance id, when a scene is loaded or there's a domain reload. It can be triggered by simple things like pressing the play button. But I do agree that it's probably not the root of your problem right now.

    I get you. Sometimes it's just not worth it.

    I want to tell you, I did a test with some of my custom editor windows, because I got a little scared reading your post. They don't use a backing SO, or things like HideFlags.HideAndDontSave. The only thing they use is serialized fields. They are recreated when another window is maximized/minimized, but they don't lose their references.

    Their references are all assets though. Does your window keep references to assets? I wonder if the problem in your case is that the GameObjects are being recreated for some reason. If that's not the problem, maybe it's the Unity version? I'm using 2019.4 right now. And if that's not the problem, well, I don't know what it could be; but maybe the notion that the issue is not happening in my case could help you in finding the root of it.
     
  7. FuguFirecracker

    FuguFirecracker

    Joined:
    Sep 20, 2011
    Posts:
    419
    UPADATE:

    Apparently this has absolutely nothing to do with UI Toolkit.
    Going back to Unity 5.6 and Utilizing IMGUI

    The same sh!7 happens with the following stripped-down code
    Code (CSharp):
    1. namespace FuguFirecracker.Bodge
    2. {
    3.     public class MindTheCubes : EditorWindow
    4.     {
    5.         public GameObject GOZero;
    6.  
    7.         [MenuItem("Tools/Mind the Cubes")]
    8.         private static void ShowWindow()
    9.         {
    10.             var window = GetWindow<MindTheCubes>();
    11.             window.titleContent = new GUIContent("Mind the Cubes");
    12.             window.Show();
    13.         }
    14.  
    15.         private void OnGUI()
    16.         {
    17.             GOZero = (GameObject) EditorGUILayout.ObjectField(GOZero, typeof(GameObject), true);
    18.         }
    19.  
    20.     }
    21. }
    Stripped down to it's bare-nakedness, I was able to reconcile my dilemma with ScritpableSingleton !

    The solution in full :
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. namespace FuguFirecracker.Utilities
    5. {
    6.     public class MindTheCubes : EditorWindow
    7.     {
    8.         public GameObject GOZero;
    9.  
    10.         [MenuItem("Tools/Mind the Cubes")]
    11.         private static void ShowWindow()
    12.         {
    13.             var window = GetWindow<MindTheCubes>();
    14.             window.titleContent = new GUIContent("Mind the Cubes");
    15.             window.Show();
    16.         }
    17.  
    18.         private void OnEnable()
    19.         {
    20.             GOZero = MindTheCubesSingleton.instance.TheDamnedCube;
    21.         }
    22.  
    23.         private void OnGUI()
    24.         {
    25.             GOZero = (GameObject) EditorGUILayout.ObjectField(GOZero, typeof(GameObject), true);
    26.         }
    27.  
    28.         private void OnDisable()
    29.         {
    30.             MindTheCubesSingleton.instance.TheDamnedCube = GOZero;
    31.         }
    32.     }
    33.  
    34.   public class MindTheCubesSingleton: ScriptableSingleton<MindTheCubesSingleton>
    35.     {
    36.         [SerializeField]
    37.         public GameObject TheDamnedCube;
    38.      
    39.      
    40.     }
    41. }
     
    ClearRoseOfWar likes this.
  8. FuguFirecracker

    FuguFirecracker

    Joined:
    Sep 20, 2011
    Posts:
    419
    Not assets, but Scene Objects ... therein lay the calamity ;)
    Solved with a ScriptableSingleton :)

    Thanks again !
     
    ClearRoseOfWar and oscarAbraham like this.