Notifications
Article
Unity EditorWindow: take a good start !
Updated 5 months ago
537
0
Little list about what you need to know to start creating your custom editor windows
In Unity, EditorWindows are the core of the editor, it's the component that parent all windows (Scene, Console, Hierarchy, ...) and they are an essential part of the development for your projects, as well for a game or anything else. It's a powerful tool that will speed up your creation and allow to non-developer to take part of your project. This article will list very useful systems and function that you must know to create a powerful and robust editor, of course i can't list them all and there are many super functions which remain hidden in the UnityEditor namespace but remember your best friend is here to help you :)

Styling

Unity editors use a Style system based on the GUIStyle class, these classes are stored in a GUISkin. the builtin unity editor skin can be fetched with EditorGUIUtility.GetBuiltinSkin or in GUI.skin which contains the default skin for your editor window. If you want to create a custom style for your editor window, you must create a new GUI Skin file in unity and configure it using the Inspector. A GUIStyle is composed of multiple states, you can see them in the picture beside, each of these states is a GUIStyleState and you can configure a custom background and text color for each, all other settings are global to the style. By default in the skin, you can configure some field like Button, Text, Label, Toggle, ... but they are not all there, if you want to modify the style of another type of field you will have to use add your new style in the customStyles array and then get them back using FindStyle or GetStyle in your script to apply them when you need to. You can use GUIStyle classes on every field functions provided by EditorGUI or EditorGUILayout classes to customize the look of your fields but there is some things that you can't modify with the style: the size of your fields. To change the size of your fields, you will need to use the GUILayoutOption array in parameter of all field functions like this: EditorGUILayout.LabelField("Hello !", GUILayout.Width(40)); These options can be cumulated and are very useful to control the size of your fields or layouts. To help you to design your editor, there is a little script that list all builtin styles and images in Unity, it is very useful and save a lot of time spent to design GUI images and background. And don't forget to look inside the class GUIStyles, there is a lot of premade styles which can speed-up the creation of your editor.

Assets loading

In your editor, you will need to load some assets like images or sounds and in unity there is a specific folder you can use to put your editor resources: Editor Default Resources. If you use it, put your resources inside a directory with the name of your project in the Editor Default Resources folder, this will prevent imported assets from override your resources. In your editor you can then load your files using EditorGUIUtility.Load or EditorGUIUtility.LoadRequired (which throw an error if the resource is missing). Alternatively you can use the Resources folder and load you assets with Resource.Load, the Resources class is more flexible and allow you to load you assets asynchronously or load all assets in one call with Resources.LoadAll, you can fond more information about loading assets here.

Event magement

In EditorWindows, you can get Keyboard/Mouse events from the OnGUI function via the variable Event.current, OnGUI will be called one times for each events in a frame. Note that the GUI drawing is also an event and is done in two steps: the first is Layout, Event.curent.type will value Layout, in this pass unity's field functions calculate the size and position of each components and then a Repaint pass draw all everything needed. By default this Repaint method get called occasionally and may cause "lag" effect but you can force it by calling the Repaint method, this must be done every time the content of your window is visually updated. Mouse/Keyboard/Command event are often thrown after a Layout event, to handle them you just have to check for Event.current.type value and then call the method Event.current.Use to tell to the window and other following methods that the event is already used (the value of Event.current.type is changed to Used). Command events are used to communicate from a window to another, for example the object picker send command backs to tell when an object was selected or validated so you can update your editor with the selected object.

Custom controls (copy/paste/find ...)

There is a special thing to do here with the implementation of these controls, you could indeed implement a copy/paste in your editor using the default Event system but you'll have to manage different keysets depending on the OS (mac is using cmd+key and windows ctrl+key). This can be pretty boring but hopefully unity implement this for us (at least for common controls), you just have to read the commandName (list of possible commands here) and implement your system behind. But if you try just reading commandName and doing if statements, you'll see that the value of commandName is the same for two or three event frames and so your commands get executed multiple times. To fix this, you need to execute your command only when you have to, here is how the command system works: first unity send an event of type ValidateCommand and during this pass you have to Use() the event otherwise the ExecuteCommand event will not occur, then in the ExecuteCommand pass, you can do what you are expected to do with the event and obviously Use() this event too. Here is an example of an implementation of a copy-paste system, this is the command system implementation i recommend:
//Copy-paste selected objects implementation Event e = Event.current; if (e.type == EventType.ExecuteCommand || e.type == EventType.ValidateCommand) { bool exec = e.type == EventType.ExecuteCommand; //Copy-paste implementation if (e.commandName == "Copy") { if (exec) Unsupported.CopyGameObjectsToPasteboard(); e.Use(); } else if (e.commandName == "Paste") { if (exec) Unsupported.PasteGameObjectsFromPasteboard(); e.Use() } }
If you're asking what is the Unsupported class, it's an utility class that contains high level editor stuff, this class is currently not in the documentation but you can have a look to his source code here.

Control ID and name system

Unity editor uses a id assigned to each controllable objects (text fields, buttons, ...) and with GUIUtility.hotControl and GUIUtility.keyboardControl you can access to the object controlled by the mouse or the keyboard in the current frame. Until you want to create a complex editor with new custom fields and behaviour, you will not need to use them and as i want to keep this article pretty simple and accessible i'll not dig in this topic but if you'r interested here is a link where this system is explained.

(Editor)GUI and (Editor)GUILayout

The GUI and GUILayout classes were used in unity for the in-game old GUI system but you can use them to create your custom editor, they contains some useful function and variables like GUI.color, GUI.Window which does not exists in their editor versions EditorGUI and EditorGUILayout. Both of these classes contains Field functions, there are many of them and will be very useful to build your editor. I will not detail them here nor the layout system (which is very simple), instead i will show you some useful functions:
  • EditorGUI.BeginCheck and EndCheck is used to know when a value in a field has been modificated between the begin and the end. You can also trigger this check by setting GUI.changed to true inside a check group.
  • Delayed fields: this type of field is very useful when the modification of your field will request a long time to process, the modification of the variable is sent only when you loose the focus on the field.
  • GUILayoutUtility.GetRect and EditorGUILayout.GetControlRect returns a Rect which, based on the layout system, can contain elements passed in parameter. This is very useful when you are creating new fields and want to integrate them in the layout system, you can do anything you want with the allocated space it will fit to the current layout.
  • The GUIUtility class have also many useful functions to rotate or scale something, get the position in your editor from the mouse or even access to the pasteboard.
  • The EditorGUIUtility class is full of useful stuff, you should have a look to every function in it !

Serialization system

When dealing with unity editors, you will have to work with the unity's serialization system and it is really not simple, especially when you have complex data structures. I strongly recommend to read this post, it explain how the system works and how to avoid all problems with it. There is a special class called SerializedObject which is used to edit a serialized object, personally i prefer accessing to the object directly but this class has some advantages like accessing to the variables by their names as strings or use the EditorGUI.PropertyField which display a field by detecting the type of the SerializedProperty.

Undo and Redo

History management in editors are pretty easy to implement thanks to the Undo class which provide anything you need to create a Undo/Redo system. This might be easy to implement but it must be implemented from the beginning of your editor because say "i'll put it later" is a big trap and will just lead to a painful implementation of the Undo system.

Bonus part

  • Enable the profiling in the Editor by clicking "Profile Editor" inside the profiler window so you can debug your performances easily. Don't forget to use the the profiler API to capture a custom frame of your code in the profiler, it's often easier to read when you know exactly what you'r profiling !
  • Detect the pro Editor with EditorGUIUtility.isProSkin
  • Render nested windows using Windows system from the EditorWindow class.
  • You can use GUIStyle.Draw to draw a background at a certain position and GUIStyle.CalcSize to calculate the size that a content will take with the current style.
  • Use ReorderableList to create a reorderable list in the editor, unfortunately there is no doc for this function instead you can read this great tutorial.
  • A super tutorial about Handles, an editor tool to change the controls you have over the scene view and more.

antoine lelievre
Procedural generation developer - Programmer
7
Comments