Custom Editor Windows - Part 1

Build your own Unity Editor windows.

The documentation has moved to: https://mitschmr-studios.io/documentation/api-guides/customeditorwindowspart1.html

This version will no longer be updated and maintained.

This topic is split into two parts. In the first part, I want to introduce you to how to build your own Unity editor windows. Part two shows you how to make custom Inspector GUIs.

Prerequisites

  • Install Unity

  • Install IDE (I use Visual Studio 2019)

  • Import Game Creator (optional, only if needed for the window)

  • Import and enable modules (optional, only if needed for the window)

Unity Editor Windows

You all know the Scene window, the Inspector and many more windows in Unity. But what is actually an Editor window?

An Editor window is a window in Unity that shows certain content. According to Unity, this includes three simple steps:

  • Create a script that derives from EditorWindow

  • Use code to trigger the window to display itself

  • Implement the GUI code for your tool

I have already written some information on Editor Windows in Inventory Custom Entries:

Normally, when a script derives from Editor and you want to start building your project, the build will fail. This is because the class Editor is not available in built versions of the game, only in the Unity Editor. We have to place the script in a folder with the name Editor, so it doesn't get included in the built game. But why the name Editor? Unity uses so called Special folder names to tread a folder's content in a special way. See Unity Special Folders for more information.

The same applies to the class EditorWindow. But it is not as easy as Unity tells you to build these windows. But let's start from the beginning.

Your first window

You should now know that we need a folder called Editor for displaying editor window. Add this folder to your scripts folder and create a new C# script. I will name my script TutorialWindowEditor.cs. Let it derive from EditorWindow, add the using statement for UnityEditor and delete the Start and Update methods. Your script should now look like this:

using UnityEditor;

public class TutorialWindowEditor : EditorWindow
{
    
}

Showing the window

In order to show a window, Unity needs a menu item which calls the ShowWindow() method. With a menu item, you are able to open a window from anywhere in top bar of the Unity Editor. The default behavior is to show already opened windows of this type. Let's do this:

[MenuItem("Window/Tutorial Window")] // Adds a menu entry under Window

public static void ShowWindow()
{
    EditorWindow.GetWindow(typeof(TutorialWindowEditor), false, "Tutorial Window");
    
    // Because we derive from EditorWindow, "EditorWindow." is not necessary, 
    // below code works too.
    // GetWindow(typeof(TutorialWindowEditor));
}

Add your class name to typeof(<your class name>). Otherwise it won't work, because Unity most likely won't find this type (class). We also added a title to the window, so it doesn't show the class name. When you switch back to Unity and let it compile, you can open your window from the path you entered in the menu item. If you click again on the button, it sets the focus on the window because there is already one opened.

Adding UI elements

Editor windows use the method OnGUI() to render the content of a window. This is where the actual code is written. There are many different elements you can add, both in EditorGUI and EditorGUILayout. The difference between them is that EditorGUILayout is auto laid out, which means it adjusts automatically to the window size.

Sometimes you also have to use GUI and GUILayout. The difference is again the auto layout. The difference between the Editor and non-Editor styles is that the Editor styles were introduced later. Let's start with a field for text and color and add a titel to each of it:

void OnGUI()
{
    EditorGUILayout.LabelField("Color");
    EditorGUILayout.ColorField(new Color(1,1,1));

    EditorGUILayout.LabelField("Text");
    EditorGUILayout.TextField("Change this text");
}

Result:

This looks ok for now, but it doesn't save the values, they get overwritten all the time. Let's change this behavior. First, add a private color and text variable before the menu item, set the GUI elements to display the values and change the values accordingly to the input.

private Color color = new Color(1, 1, 1); // You can set any color you want
private string text = "Change this text"; // You can set any text you want

<...>

void OnGUI() 
{
    EditorGUILayout.LabelField("Color");             // Title
    color = EditorGUILayout.ColorField(color);       // Color field

    EditorGUILayout.LabelField("Text");              // Title
    text = EditorGUILayout.TextField(text);          // Text field
}

When we now change the values, they are saved until the window gets closed.

Using the correct method

Editor windows contain similar methods like gameobjects. Visit EditorWindow for all methods and more. Here is a list of the (in my opinion) most important methods with the description and their use cases:

Name

Description

Use cases

Awake()

Called as a new window is opened

Execute code when window is opened

Close()

Closes the window

Close window

GetWindow()

Gets a window of a certain type

Show already opened window / create new window

OnDisable()

Called when window goes out of scope

Clear / save variables

OnEnable()

Called when object is loaded

Load variables / data

OnGUI()

Render your window code

Window design

Repaint()

Repaints the window

Update window if content has changed

Example: Reading data from disk should NOT happen in OnGUI, because OnGUI gets called up to several times per frame. Do this in i.e. OnEnable(), which gets only called when the window is loaded.

More advanced examples

You still need the basic class, menu item, ShowWindow() etc.

Toolbar

private static readonly GUIContent[] TAB_NAMES = new GUIContent[]
{
    new GUIContent("Tab 1"),
    new GUIContent("Tab 2"),
    new GUIContent("Tab 3"),
    new GUIContent("Tab 4")
};

private int tabIndex = 0;

void OnGUI()
{
    tabIndex = GUILayout.Toolbar(tabIndex, TAB_NAMES);

    switch (tabIndex)
    {
        case 0: DoSomething1(); break;
        case 1: DoSomething2(); break;
        case 2: DoSomething3(); break;
        case 3: DoSomething4(); break;
    }
}

// Void DoSomething1(), DoSomething2()...

Scrollarea with custom font and dummy text

private Vector2 _scrollPosition;
private GUIStyle _tutorialFont = new GUIStyle();

void OnEnable()
{
    _tutorialFont.fontSize = 13;
    _tutorialFont.wordWrap = true;
    _tutorialFont.margin = new RectOffset(5, 5, 0, 0);
}

void OnGUI()
{
    _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);

    // Split for better readability
    EditorGUILayout.LabelField("Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, " +
        "sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. " +
        "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. " +
        "At vero eos et accusam et justo duo dolores et ea rebum. " +
        "Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
        , _tutorialFont);

    EditorGUILayout.EndScrollView();
}

Custom Object fields

private Animation _animObject;
private Actions _actionObject;
private ScriptableObject _scriptableObject;

void OnGUI()
{
    _animObject = (Animation)EditorGUILayout.ObjectField(_animObject, typeof(Animation), false);
    _actionObject = (Actions)EditorGUILayout.ObjectField(_actionObject, typeof(Actions), true);
    _scriptableObject = (ScriptableObject)EditorGUILayout.ObjectField(_scriptableObject, typeof(ScriptableObject), false);
}

Last updated