Custom Editors in Unity3D – Part 9: EditorWindow

Whew, 9 posts later and here we are at the last major topic in my Unity3D Custom Editors series!  Today we’ll be talking about EditorWindows – a window inside of Unity that we can use to do whatever we want!

EditorWindow Basics

First off, let’s briefly cover what an Editor Windows is:  an Editor Window is pretty much any self contained window/pane inside of the Unity Editor.  The Scene View, Inspector, Console, Hierarchy, etc are all examples of EditorWindows – they can be docked, tabbed, moved around – and EditorWindows that you write are no different: they get all this behavior built-in!

Now, how do we get started writing an EditorWindow?

Editor Window Class

1) As with any of our other Editors, we’ll want to add a script file below an ‘Editor’ folder in our project, with the name of the editor window class we want to make (I tend to have class names for EditorWindows end in ‘Window’, but that’s not necessary for them to work).

2) Our class should extend EditorWindow, though in this case we don’t have any attributes we have to use on it (since it’s not editing a specific type of data).

3) Add an OnGUI() function to our Editor Window class in which to put all of our GUI code (Note: it’s not an override, just make sure you have a void function with that name, much like Start or Update in a MonoBehavior).

Instantiating our Window

Now, since an EditorWindow doesn’t target any specific piece of data, there is no default place inside of the editor where your new window would show up – so how do we get it to display?  We do so by using the EditorWindow.GetWindow(Type) function.  GetWindow() is a special function in the EditorWindow class that takes the type of your EditorWindow as a parameter, and will either return and display the existing instance of it, if it exists, or create a new instance for you.

That’s all well and good, but you may be wondering where we actually call GetWindow from?  Well, you could do it from any piece of editor code you want (a PropertyDrawer, CustomEditor, a ContextMenu call, etc), but the most common way to do this for stand-alone Editor Windows is to add a static function inside of your editor window class, and use the MenuItem attribute to add it to Unity’s menu, as demonstrated in the following code snippet:

[MenuItem("Window/GameBoard Editor &g")]
static void CreateWindow()
{
     // Get existing open window or if none, make a new one:
     GameBoardEditorWindow window =
          (GameBoardEditorWindow)EditorWindow.GetWindow(typeof(GameBoardEditorWindow));
}

In the above code snippet (inside of the GameBoardEditorWindow class), we simply call GetWindow to display an instance of GameBoardEditorWindow, and the function is accessible via the Window/GameBoard Editor menu command:

WindowMenu

 Shortcuts

You may have noticed that in the MenuItem() attribute declaration, there’s a little bit more to that string than “Window/GameBoard Editor”, and in the screenshot above, you probably noticed that GameBoard Editor has a keyboard shortcut – Option(/Alt) G.  The “&g” at the end of the string we passed into the MenuItem attribute tells Unity that this menu item can also be accessed by holding Alt (indicated by the &) and pressing ‘g’ (the ‘g’).

The specifics for all the shortcut options can be found at Unity’s MenuItem Script Reference page, but basically, a shortcut is a combination of a character representing a modifier key (‘&’ for Option/Alt, ‘%’ for Ctrl/Cmd, ‘#’ for Shift, and ‘_’ for no modifier) and a key on the keyboard (a single letter/character for letters and numbers).  One little ‘gotcha’ that’s easy to miss at first with shortcuts is that you MUST have a modifier character (if you don’t want to use a modifier key, use ‘_’ for nomod) or they won’t work.

Example

I’ve written an example EditorWindow, the source for which I’ll include at the end of this post, but since we’ve covered a lot of Editor GUI stuff pretty thoroughly I don’t want to go into too much depth here.  The example EditorWindow is an alternate way of editing GameBoards from the last post, where we took a look at CustomEditors, and the end result looks like this:GameBoardEditorWindow

You’ll notice the GameBoard editing portion looks pretty similar to the last example, but I’ve added the ‘side bar’ on the left (easily accomplished with EditorGUILayout) with some added functionality, as well as a few extra buttons inside of the editor itself.

One thing to note (which you’ll probably see if you look at the source for this example) is that instead of editing a GameBoard component (which we wrote in the CustomEditor example), I’ve created a standalone class called GameBoardData which has all of the board state related information/functions from GameBoard, but none of the Component specific stuff (visual prefabs, tile size, etc).  GameBoardEditorWindow has an instance of GameBoardData that it edits, and methods for loading/saving that data from/to a GameBoard object in the scene (which we’ll look at in a minute).

Note: For ease of breaking up the examples, GameBoard  has a function to pull data from a GameBoardData, and GameBoardData has a function to pull data from a GameBoard, though in a ‘real’ project I’d advise just having your GameBoard have a GameBoardData object as a member, rather than storing the data directly in the component.

Changes from GameBoardEditor

As far as added functionality, there are several things added to this editor that aren’t present in GameBoardEditor, which we wrote last time:

-The first two buttons in the sidebar, Fill and Clear, are fairly self explanatory: Fill fills the board with the currently selected GameBoardTile, and Clear fills it with EMPTY.

-There are Arrows at the beginning of each row and the top of each column in the board editing section.  These fill the specified row or column with the currently selected value for GameBoardTile.

-Finally, the last two buttons in the sidebar(which are greyed out in the screenshot above) allow us to send the current GameBoardData to a currently selected GameBoard in the scene, or load the GameBoardData we’re editing from a currently selected GameBoard in the scene (in the screenshot above, no GameBoard is selected, so the buttons are greyed out).

The first two bits are pretty easy to do with information we’ve already covered, so I’ll let you poke around the source code to see how they work, but the last one brings me to a handy class to use with all your editor code, but especially so with EditorWindows: the Selection class.

Selection

Selection is a class provided by UnityEditor that provides access to data about what is currently selected in the scene.  For an example, let’s look at how the Save/Load buttons work on GameBoardEditorWindow to interact with GameBoards in the scene:


GameBoard selectedObjectBoard = null;

if (Selection.activeGameObject != null)
{
     selectedObjectBoard = Selection.activeGameObject.GetComponent<GameBoard>();
}

//...

bool guiEnabled = GUI.enabled;
GUI.enabled = selectedObjectBoard != null;

if (GUILayout.Button("Save to Object"))
{
     selectedObjectBoard.Load(currentData);
}
if (GUILayout.Button("Load From Object"))
{
     currentData.Load(selectedObjectBoard);
}

GUI.enabled = guiEnabled;

First off, we use Selection.activeGameObject to see if there is an object selected in the scene, and if so, grab the GameBoard component on that object, and store it in selectedObjectBoard.  Later on, we use selectedObjectBoard to tell if there’s a GameBoard active to Save to/Load from, and use GUI.enabled to enable/disable the associated buttons accordingly (note the use of guiEnabled to cache the value before we set it, and return it to its former state after we’re done – this is good housekeeping to make a habit of in order to make sure you don’t break other GUI code that’s running elsewhere and using GUI.enabled!).  If the buttons are active and clicked, we then use selectedObjectBoard to actually save/load the data as necessary.

 

…And I think that about wraps up EditorWindows!  If you’d like to take a look at the source code from this example, here is a link to GameBoardEditorWindow and GameBoardData.

Conclusion

As I mentioned at the beginning of this post, this is the last ‘major’ topic in my series of posts on Unity3D Editors, but I’ll be posting a ‘summary’ soon with links to each of the posts, as well as a download link for a Unity Project with all the examples from this series, plus a few extras, in case you want to play around with running versions of the examples but don’t feel like piecing the project together.  I’ve also got a few ‘bonus’ topics that I’ll be posting about in the next few days, but they’re not directly ‘custom editor’ related, so I think of them more as appendices than part of the main series.

I hope some or all of these posts have been helpful, and as always, if you have comments, questions, or suggestions about this post, any other post in the series, or things you’d like to hear more about, feel free to leave a comment below!

 

Leave a Reply

Your email address will not be published. Required fields are marked *