Search Unity

Editor Extension - Persistent Data

Discussion in 'Immediate Mode GUI (IMGUI)' started by Palidor, Oct 12, 2010.

  1. Palidor

    Palidor

    Joined:
    Aug 31, 2010
    Posts:
    9
    Hi All,

    Not sure if this is the right section for this kind of section, so I apologize in advance if it isn't. I've been tasked with making an extension to the Unity editor for some of our custom software we have. Although i'm getting well-versed in how the editor extension classes work, I'm having a problem determining where one would initialize data that needed to be loaded outside of the game for the editor to work. For example:

    I have a large data file that needs to be loaded when a scene is opened, and the file exists in the scene. When the editor window is opened, it will look inside this "data class" that was loaded, and populate a bunch of drop downs based on the data. My issue is that currently I am loading this data in the Init function of the EditorWindow extension. This seems like bad practice, as the data gets cleared out when i close the EditorWindow, and also gets randomly cleared when I press the Play button.

    Is there a good place to do this one-time initialize of a class from this data file so that it persists regardless of whether i am in editor or play mode? Any help would be greatly appreciated!

    Sean
     
  2. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    The EditorApplication class lets you define a callback function to assign to its Update property. This gets called many times a second during the operation of the editor. You could use this to check which scene is currently open (with the currentScene property) and load in your data whenever you detect the scene has changed.
     
  3. Palidor

    Palidor

    Joined:
    Aug 31, 2010
    Posts:
    9
    Thanks Andeee!

    Where would I put the data construct that the data needs to be loaded into? Currently, it's a member variable of the EditorWindow itself, which seems very poor design-wise, as this causes the data to get deleted when i close the editor window. Do you have a suggestion as to where I should have this reside at?
     
  4. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    Could you put the data in static variables of the editor window class? That way, they won't depend on an instance of the window.
     
  5. Palidor

    Palidor

    Joined:
    Aug 31, 2010
    Posts:
    9
    I attempted to fix the issue using your suggestion, but the problem seems to lie elsewhere. I ensured that all of my member variables were static, and proceeded to open the editor window. Everything functioned as expected, until I pressed the Play button. Upon doing this, the window seemed to lose it's internal data, as if the window's OnDestroy or Destructor was called. This caused all data to be wiped out. I must be really missing something big here. Is there anywhere besides the class itself that i can keep this data, that is ensured to only be called once at a startup of some sort?

    Thanks again for your help Andeeee. i really appreciate it!
     
  6. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    Can you post the code that is giving you the problems? It's not easy to guess what might be going wrong without seeing it.
     
  7. DavidM

    DavidM

    Joined:
    Aug 19, 2010
    Posts:
    4
    I am seeing this as well.

    It doesn't make sense that the Editor Window Object would be cleared out just because the window is closed or the Game is Run.
    Runs very counter intuitive to making Game building Tools...
     
  8. DavidM

    DavidM

    Joined:
    Aug 19, 2010
    Posts:
    4
    Sorry to jump in here, I fixed my issue.

    I set the texture in the Inspector and took out my code to explicitly load the texture and it works.

    Still seems odd they get nulled out just by flipping to another window tab and back....
     
  9. Palidor

    Palidor

    Joined:
    Aug 31, 2010
    Posts:
    9
    Andeee - I can't post any code here, as it would be a breach of confidentiality at work. However, I can give this pseudocode that will give you an idea:

    public class Object : EditorWindow
    {
    private SomeManager manager = null

    [MenuItem("Window/Object")]
    static void Init()
    {
    // Create and setup window
    // Create SomeManager
    manager = new SomeManager();
    manager->Initialize("filepath");
    }
    }

    When the window is closed, this manager gets cleaned up.... it also gets randomly "nulled" out when the editor window is open, and I press the Run button. And vice versa, if it's opened while the game is running and I press Stop.

    This seems very strange that this would happen. It's causing many problems with the system we are trying to achieve.

    DavidM - It seems you had the same issue as me, but now your data isn't relying on the editor window anymore? Perhaps this is a bug in Unity that they don't know about?

    I'm at a big loss here as far as what to do. I've considered initializing this data in a singleton, and having the singleton initialized only once in the static Init function. But is it even possible to have a singleton exist in edit mode? I know it can in game, but I've yet to try in edit mode.

    Sean
     
  10. DavidM

    DavidM

    Joined:
    Aug 19, 2010
    Posts:
    4
    I had some stuff typed and it looks like the browser ate it.

    I initially had 3 texture2D static variables which I tried to set in the Init() method. This seemed to not work at all.

    I switched to using regular instance variables and set those in the Inspector for the script. This works ok, except when you change the values in the inspector you must close and re-open the window.

    I think this is because the Inspector values are "default references", so they can be copied into the Editor Window object at various times.

    It appears to be when flipping between windows and script recompiles, but other times when I change those references I have to close and re-open the Editor Window to get the new values.

    Seems erratic but probably makes sense if you were looking at the framework code I guess.

    -David
     
  11. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    My suggestion was essentially that you change the code to this:-
    Code (csharp):
    1. public class Object : EditorWindow
    2. {
    3.   static SomeManager manager
    4.  
    5.   [MenuItem("Window/Object")]
    6.   static void Init()
    7.   {
    8.     // Create and setup window
    9.     // Create SomeManager
    10.     manager = new SomeManager();
    11.     manager->Initialize("filepath");
    12.   }
    13. }
    (It is only the manager object that needs to be in a static variable as far as I can tell.) I would expect this to avoid the manager object being dependent on any particular instance of the window class. Is this what you tried (but still found it didn't work)?
     
  12. AndrewT

    AndrewT

    Joined:
    Nov 30, 2010
    Posts:
    3
    I'm getting the same problem.

    My EditorWindow derived class contains a System.Collections.Generic.Dictionary object that I'm using to cache information about game objects as they are selected.

    e.g. In the OnSelectionChange function I look to see if the object's name is already in the Dictionary and if it isn't then I go and gather the information I want and add it to the Dictionary so I can access it directly next time.

    But, whenever I hit Play the Dictionary object appears to get wiped completely and my editor class then has to go and gather all the information again.

    I've tried making the Dictionary object static and I've tried calling DontDestroyOnLoad(this) inside the OnSelectionChange function but these have had no effect.

    Suggestions about what I'm doing wrong greatly appreciated!

    Edit: Some more info...

    Looking at the EditorWindow class docs I thought I'd add Debug.Log output to the OnEnable and OnDisable functions to see when/if they are getting called. I also output the size of my Dictionary object in both Debug.Log calls.

    When Play is pressed I see an OnDisable (with the correct size of my Dictonary object) followed by an OnEnable (with a size of 0 for my Dictionary object).

    I've even moved my Dictionary object to be a public static in a separate class and it still gets cleared between the OnDisable and OnEnable calls!
     
    Last edited: Nov 30, 2010
  13. ilcygnet

    ilcygnet

    Joined:
    Sep 10, 2009
    Posts:
    28
    check out your all data object use System.Serializable attribute and inherit System.Object in your custom classes, for example

    [System.Serializable]
    public class Something : System.Object
    {
    public int someVariable;
    .....
    }

    if you miss it, Unity editor sweeps out all unserializable data types.
    and also please keep in mind these rules:

    1. declare your data types to be serializable. ( described above. )
    2. use all variables in public. unity editor forgets all private data. ( if you don't want to display them in inspector, try [HideInInspector]
    3. DO NOT USE HEAP ALLOCATION. if you want your data to be permanent, make them as a scene element or storage file.
    4. use array instead of ArrayList or Hashtable. those data storage classes are not serializable. try this: int[], float[], Vector2[]

    my answer is, i think, not perfect. i'm still trying to find a right answer :) thx!
     
  14. goodhustle

    goodhustle

    Joined:
    Jun 4, 2009
    Posts:
    310
    Wow, this totally saved me just now. I was beginning to think custom data types were impossible. Thanks.
     
  15. Zogg

    Zogg

    Joined:
    Mar 28, 2009
    Posts:
    158
    I have a similar problem: Objects being destroyed when hitting Play.

    I've an EditorWindow which contains an array populated by objects of class Stuff. This class contains, again, an array of objects of class Thingy. Boiled down it gives something like this:

    Code (csharp):
    1.  
    2. [System.Serializable]
    3. public class MyEditorWindow: EditorWindow
    4. {
    5.     public Stuff[] maArrayStuff;
    6.  
    7.     ...
    8. }
    9.  
    10.  

    Code (csharp):
    1.  
    2. [System.Serializable]
    3. public class Stuff ScriptableObject
    4. {
    5.     public Thingy[] mArrayThingies;
    6.  
    7.     ...
    8. }
    9.  

    Code (csharp):
    1.  
    2. [System.Serializable]
    3. public class Thingy: ScriptableObject
    4. {
    5.     ...
    6. }
    7.  
    I should mention that all instances of Stuff or Thingy are created using ScriptableObject.CreateInstance.

    The problem is that when I hit the Play button, the Stuff objects in maArrayStuff persist (yay!), but their Thingies are destroyed and replaced by nulls in mArrayThingies.

    I tried to make maArrayStuff static but this only made things worse (the Stuff objects were destryed as well).

    Halp! :(
     
  16. Zogg

    Zogg

    Joined:
    Mar 28, 2009
    Posts:
    158
    Note : To exclude the case that the thingies are deleted somewhere else in my code, i just created three test classes which reduce what I described above to the bare bones. The problem persists.

    Could someone help me ? Prettyplease.
     
  17. Zogg

    Zogg

    Joined:
    Mar 28, 2009
    Posts:
    158
    I tried to solve the problem by saving the data in OnDisable() and reloading it in OnEnable(). Unfortunately, the data are deleted after OnEnable(), so this doesn't work. Bummer.

    I would really appreciate some help on this issue. :(
     
  18. mikeschuld

    mikeschuld

    Joined:
    Mar 9, 2013
    Posts:
    1
    Has anyone come up with a solution to this problem yet? I am working on adding scene based Unit testing into my project and my tests can load and run fine, but whenever a test tells the scene to start playing, everything gets wiped in the manner that all of you have described in this thread.
     
  19. IzzySoft

    IzzySoft

    Joined:
    Feb 11, 2013
    Posts:
    376
    Still happens.

    Hit Play... hit breakpoint in OnEnable
    no issues and the reference shows a valid Texture2D while inside OnEnable(),
    but as soon as you resume, and check OnGUI... that public reference is NULL.

    Code (csharp):
    1.  
    2. [System.Serializable]
    3. public class NineSlice_SerializedData : System.Object
    4. {
    5.     public Texture2D tx;
    6. }
    7.  
    ...and...

    Code (csharp):
    1.  
    2. public class NineSlice : EditorWindow {
    3.  
    4. public NineSlice_SerializedData nssData = new NineSlice_SerializedData();
    5.  
    6. [MenuItem("Example/9 Slice")]
    7. public static void ShowWindow () {
    8.     EditorWindow.GetWindow <NineSlice>().Show();
    9. }
    10.  
    11. void OnGUI() {
    12.     if( nssData == null )
    13.         Debug.Log( "nssData == Null" );
    14. }
    15.  
    16. void OnEnable() {
    17.     nssData.tx = new Texture2D( 16, 16, TextureFormat.ARGB32, false, false );
    18. }
    19.  
    20. void OnDisable() {
    21.     // ...
    22. }
    23.  
    24. }
    25.  
     
    Last edited: Dec 31, 2013
  20. samizzo

    samizzo

    Joined:
    Sep 7, 2011
    Posts:
    487
    I'm seeing this too. My EditorWindow.OnEnable method is called when I hit play, but my textures are already valid at that point. However, at the very next OnGUI call the textures are again null. I'm working around this by recreating the textures in OnGUI if necessary.