In this tutorial you’ll learn how to extend the Unity3D’s editor so you can make better use of it in your project. You’ll learn how to draw your own gizmo, create and delete objects in code, create editor windows, use components, and allow the user to undo any action they take with your script.
This tutorial assumes you already know the basics of Unity workflow. If you know how to create objects, prefabs, scenes, move around in the editor, attach components then you are good to go!
Final Result Preview
Let’s take a look at the final result we will be working towards:
As you can see, we’ll be creating an editor window, and a color picker whose selection we’ll use to draw a grid. We’ll also be able to create and delete objects, snapped to this grid, and undo such actions.
Step 1: Gizmos
First we’ll learn how to use gizmos. Here are a few examples of built-in gizmos.
This is the one you’ll probably see the most in Unity, since it’s drawn for every object that has a Transform
component attached to it – so basically every selected object will have this gizmo drawn.
Here’s another gizmo, which enables us to see the size of the BoxCollider
attached to our game object.
Step 2: Create a Grid Script
Create a C# script that we can use to draw our own gizmo for an object; we’ll draw a simple grid in the editor as an example.
0102030405060708091011121314using
UnityEngine;
using
System.Collections;
public
class
Grid : MonoBehaviour
{
void
Start ()
{
}
void
Update ()
{
}
}
For a grid we need to add two variables, the width and the height.
04050607080910111213141516public
class
Grid : MonoBehaviour
{
public
float
width = 32.0f;
public
float
height = 32.0f;
void
Start ()
{
}
void
Update ()
{
}
}
To draw in the editor we need to use OnDrawGizmos
callback, so let’s create it.
0405060708091011121314151617181920public
class
Grid : MonoBehaviour
{
public
float
width = 32.0f;
public
float
height = 32.0f;
void
Start ()
{
}
void
Update ()
{
}
void
OnDrawGizmos()
{
}
}
Step 3: Draw the Grid
To draw a grid we need a set of horizontal and vertical lines and the position of the editor’s camera so we know around which point we should draw our grid. First, let’s save the camera’s position to a separate variable.
17181920void
OnDrawGizmos()
{
Vector3 pos = Camera.current.transform.position;
}
As you can see, we can get the editor’s camera by using the Camera.current
reference.
Now we’ll need two for loops that will draw the horizontal and vertical lines.
17181920212223242526272829303132void
OnDrawGizmos()
{
Vector3 pos = Camera.current.transform.position;
for
(
float
y = pos.y - 800.0f; y < pos.y + 800.0f; y+= height)
{
Gizmos.DrawLine(
new
Vector3(-1000000.0f, Mathf.Floor(y/height) * height, 0.0f),
new
Vector3(1000000.0f, Mathf.Floor(y/height) * height, 0.0f));
}
for
(
float
x = pos.x - 1200.0f; x < pos.x + 1200.0f; x+= width)
{
Gizmos.DrawLine(
new
Vector3(Mathf.Floor(x/width) * width, -1000000.0f, 0.0f),
new
Vector3(Mathf.Floor(x/width) * width, 1000000.0f, 0.0f));
}
}
To draw lines we use Gizmos.DrawLine()
. Note that the Gizmos
class has a lot of other drawing API methods, so it is possible to draw such primitives as cube or sphere or even their wireframes. You can also draw an image if you need to.
The grid lines should be infinitely long but float.positiveInfinity
and float.negativeInfinity
didn’t seem to work well with drawing the lines, so we can simply put arbitrarily large numbers instead of those. Also, the number of lines strictly depend on the constants we put in the for
loops’ definitions; technically we shouldn’t leave those constants like that but it’s just a test code.
To see the grid, create an empty object and attach our script to it:
Step 4: Create a Custom Inspector
The next thing to cover is customizing the inspector. To do that we need to create an editor script. Create a new C# file and name it GridEditor. This script should be placed in the Editor folder; if you don’t have one then create it now.
12345678using
UnityEngine;
using
UnityEditor;
using
System.Collections;
[CustomEditor (
typeof
(Grid))]
public
class
GridEditor : Editor
{
}
This time we also need to use UnityEditor
to be able to make use of the editor classes and functions. To override the default inspector of our Grid
object we need to add an attribute before our class declaration,[CustomEditor (typeof(Grid))]
lets Unity know that we’ll be customising the Grid
‘s inspector. To be able to use the editor callbacks, we need to derive from the Editor
class instead of MonoBehaviour
.
To change the current inspector we need to override the old one.
060708091011public
class
GridEditor : Editor
{
public
override
void
OnInspectorGUI()
{
}
}
If you check the grid object’s inspector in the editor now, it’ll be empty even though the object itself has some public members. That’s because by overriding the OnInspectorGUI()
we discarded the default inspector in order to make a custom one instead.
Step 5: Use GUILayout to Fill the Custom Inspector
Before we create any fields we need to get a reference to the object that inspector applies to. We actually have its reference already – it’s named target
– but for convenience we’ll create a reference to the Grid
component of that object. First, let’s declare it.
678public
class
GridEditor : Editor
{
Grid grid;
We should assign it in OnEnable()
function which is called as soon as the inspector is enabled.
0607080910111213public
class
GridEditor : Editor
{
Grid grid;
public
void
OnEnable()
{
grid = (Grid)target;
}
Let’s create some inspector fields now. We’ll use GUILayout and EditorGUILayout classes for that.
15161718192021public
override
void
OnInspectorGUI()
{
GUILayout.BeginHorizontal();
GUILayout.Label(
" Grid Width "
);
grid.width = EditorGUILayout.FloatField(grid.width, GUILayout.Width(50));
GUILayout.EndHorizontal();
}
The first line, GUILayout.BeginHorizontal();
indicates that we want to place the following inspector elements next to each other, left to right. As you may imagine, the last line,GUILayout.EndHorizontal();
indicates that we no longer want to do that. The actual items are in between those two lines. The first one is a simple label (in our case it will be displaying Grid Width text), and then next to it we create a EditorGUILayout.FloatField
which is as you may imagine a float field. Note that we’re assigning grid.width
to the value of that FloatField
, and the float field itself shows the value of grid.width
. We also set its width to 50
pixels.
Let’s see if the field is added to the inspector:
Step 6: Fill the Inspector and Repaint the Scene
Now let’s add one more item to the inspector; this time it will be grid.height
.
151617181920212223242526public
override
void
OnInspectorGUI()
{
GUILayout.BeginHorizontal();
GUILayout.Label(
" Grid Width "
);
grid.width = EditorGUILayout.FloatField(grid.width, GUILayout.Width(50));
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label(
" Grid Height "
);
grid.height = EditorGUILayout.FloatField(grid.height, GUILayout.Width(50));
GUILayout.EndHorizontal();
}
That’d be all for our grid object fields, if you want to know about other fields and items that you can use in the inspector then you can visit the Unity reference pages on EditorGUILayout and GUILayout.
Note that the changes we make in our new inspector are visible only after we select the Scene View window. To make them visible once they are made we may call SceneView.RepaintAll()
.
1516171819202122232425262728public
override
void
OnInspectorGUI()
{
GUILayout.BeginHorizontal();
GUILayout.Label(
" Grid Width "
);
grid.width = EditorGUILayout.FloatField(grid.width, GUILayout.Width(50));
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label(
" Grid Height "
);
grid.height = EditorGUILayout.FloatField(grid.height, GUILayout.Width(50));
GUILayout.EndHorizontal();
SceneView.RepaintAll();
}
Now we don’t have to click outside the inspector to see the results of the changes.
Step 7: Handle the Editor Input
Now let’s try to handle the editor’s input, just like we would do it in the game. Any key or mouse states should be available to us. To have this functionality we have to add a onSceneGUIDelegate
callback to ourSceneView
. Let’s call our update function GridUpdate()
.