- Home /
Outsource EditorWindow code
My current project is a 2D platformer for which I'm building a level-editor by using an EditorWindow.
In previous projects where I created editor tools I noticed that this can quickly lead to very long classes (2000+ loc) which is why this time I want to outsource certain functions into their own classes (e.g FileManagerPanel, LevelInformationPanel, BackgroundLayerPanel, etc) and just call the Draw method I implemented for each of them from within the main EditorWindow. References to instances of these Panel classes are created when the EditorWindow is opened (this way I can later implement Dependency Injection).
The problem is that some Panels still need to access other Panels (e.g. the LevelInformationPanel needs to read data from/write data to a ScriptableObject which is loaded in the FileManagerPanel). When everything was still in the same EditorWindow class I would just create a new SerializedObject and call its Update method, then add EditorGUILayout.PropertyField(serializedObject.FindProperty("VariableName"));
and afterwards call the ApplyModifiedProperties method.
This works because the EditorWindow class itself derives from UnityEngine.Object and thus can be serialized. With my outsourced classes however, I noticed that it's apparently not a good idea to inherit from UnityEngine.Object directly because the constructor will return null. When deriving these classes from System.Object instead the constructor will work but not the PropertyField.
I think ScriptableObject would probably work but I'd need to create file instances within my Asset folder which is kind of ugly. Also maybe PropertyDrawers would work but I don't like using them because it's annoying having to specify all the rects myself instead of being able to use the Layout classes. Is there maybe another option?
Answer by Bunny83 · Nov 27, 2017 at 10:37 PM
An EditorWindow is also just a ScriptableObject. You should never derive any class from UnityEngine.Object. It's just the base class for all objects that have a counterpart on the native C++ side. The only classes you should use as your baseclasses at runtime are MonoBehaviour and ScriptableObject. Of course in editor code we have some additional classes like EditorWindow and Editor (which are actually derived from ScriptableObject).
So you can just derive your classes from ScriptableObject. This will make your classes survive an assembly reload. If you create them in OnEnable only if they do not exist yet everything should be fine. Of course they won't survive a scene change or closing Unity. If you want them to survive that as well you have to store them as assets.
ps: You can still use SerializedObject / SerializedProperty as long as your desired field is in a serialized class that is part of the EditorWindow. You just need to use the correct property path. So you don't deal with the actual "panel class" but access it through a SerializedObject of the EditorWindow that is referencing your panel.
However we don't know enough about what kind of interactions you need between your classes. Also do you necessarily need a PropertyField? If you just want to display an GUI block for your panel just use GUILayout and EditorGUILayout controls for the things you actually need to display.
Depending on the usage it might also be a solution to actually make seperate EditorWindows. That way the user can dock them the way he likes. $$anonymous$$eep in $$anonymous$$d that you can "find" any UnityEngine.Object with FindObjectOfType if you want to make cross references..
Ah, of course, thanks!
Yeah, I'm aware of the C++ side needed for the serialization. It seems I just didn't think about creating a ScriptableObject without saving the asset since in my previous projects I always needed to save them. However this time around I actually specifically don't want to save certain settings so that the user of the level-editor will always be presented with a fresh state when he opens it up.
About the ps:
Good call. I think with this I'll be able to have a reference to the main EditorWindow in the base class of the Panels and save 1-2 reference calls.
Since this is for a level-editor I'll need a few different data types (e.g. Texture2D), hence the PropertyField, at least for now while still testing some things.
I also thought about using different EditorWindows to give the user more freedom but decided not to do that for the moment since all panels will be needed at all times for the level-editor anyway.
$$anonymous$$aybe later I'll add a toggle for this. I've seen there's an overload for the EditorWindow.GetWindow method which lets you specify another EditorWindow besides which the new one should be opened. I haven't tried it yet and the documentation is a bit vague but I suppose it docks the tab of the new EditorWindow as inactive behind the specified one, right? Though this means that the user will be presented with one long row of tabs (currently 10 Panels) which he then can sort himself (positive) but also has to sort himself (slightly negative). What would be great would be a way to get the information of which EditorWindows are docked in horizontal/vertical "split screen" view in relation to which other ones. If this doesn't exist or proves to be too complicated I can happily leave it out though, since this is pretty low on my todo list for now.
Cheers!
But a PropertyField is never really needed. The only things which are a bit of a pain are UnityEvents and maybe arrays (though arrays and Lists are actually not that hard).
If you need a Texture2D field you just use an ObjectField. For any UnityEngine.Object type you can use an ObjectField. A PropertyField in the end does just use all those elements you'll find inside the EditorGUILayout class. (IntField, FloatField, Slider, Vector3Field, ...). A PropertyField is useful for generic property handling and when you want to use PropertyDrawers. However if you know the type and have access to the actual class it's actually faster and more flexible to use the proper GUI control directly.
Yes the scripting support for docking is still quite bad. $$anonymous$$ost of the required classes are internal. So currently it's not possible (without heavy use of reflection) to really create a splitview manually and arrange editor windows the way you want.
GetWindow is the "normal" way to actually create and open an editor window. It will ensure to reuse an existing window so it never opens a second window if there is already one open. I have never used the "desiredDockNextTo" parameter.
Your answer
Follow this Question
Related Questions
how to stop reorderable list from passing Event value to the new element 1 Answer
Create a custom window and set his position on second screen 1 Answer
Can't remove using Unity Editor, it is necessary for the development of the game. 0 Answers
Error: Unable to load the icon: 'CustomEditorWindow'. (Custom Scene View) 1 Answer