Quantcast
Channel: Unity Patterns
Viewing all articles
Browse latest Browse all 10

Customizing the Editor Part 3 – Inspectors & Editors

$
0
0

If you haven’t covered the first two tutorials in this series, I recommend reading those before going forward:

In this tutorial I will show you how, with editor scripts, you can not only modify the behaviour of Unity’s scripts and menus, but its inspector window as well.

A Simple Script & Inspector

To demonstrate this, let’s first create a simple script:

using UnityEngine;

public class MyScript : MonoBehaviour
{
	public float radius;

	void OnDrawGizmos()
	{
		Gizmos.color = Color.red;
		Gizmos.DrawWireSphere(transform.position, radius);
	}
}

This script has a single variable, radius, and draws a wire-sphere to the scene view using gizmos (explained in the first tutorial). If we place this script on an Empty GameObject, we should be able to modify radius and see something like this:

Screen Shot 2015-02-06 at 1.04.58 PM

But what if, for example, you wanted to force “radius” to be between 0 and 10, how could you do it? As it turns out, Custom Editors are the perfect solution.

Actually, PropertyDrawers are the perfect solution, but I’m saving those for the next tutorial!

A Custom Editor for Our Script

A Custom Editor in Unity is a script that runs only in the editor, never in your released game, and can be used to control what appears in the Inspector and Scene View for a particular component.

To see this in action, first let’s create a new script called MyScriptEditor.cs, as it is an editor for MyScript.cs. Because this is an editor script, we have to put it in an Editor/ folder for it to work properly.

Screen Shot 2015-02-06 at 1.11.41 PM

The script is going to be different from a regular MonoBehaviour script, and instead will start out like this:

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{

}

First, notice that I am using UnityEditor as well as UnityEngine. Second, instead of extending MonoBehavour, this particular script is going to extend Unity’s Editor class.

Finally, we have added a [CustomEditor] attribute to this class. This attribute tells Unity which script that this is an editor for. If you wanted, you could have a custom editor for every different script in your game!

OnInspectorGUI()

If you select your MyScript object in the scene again, nothing should be any different. In order to start modifying the inspector, we have to override the Editor’s OnInspectorGUI() function:

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
	public override void OnInspectorGUI()
	{

	}
}

Once you’ve done this, re-select your MyScript object and its inspector should now look like this:

Screen Shot 2015-02-07 at 1.54.32 PM

Empty! The OnInspectorGUI() function is in charge of displaying what appears here, so if we put nothing in it, that’s what we get. If we want it to display the default inspector, we can call the base function…

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
	public override void OnInspectorGUI()
	{
		//This will now draw the default inspector GUI
		base.OnInspectorGUI();
	}
}

But if we want to add more to the inspector, we can do so by using Unity’s GUI drawing classes such as GUILayout and EditorGUILayout. For example, I could add a couple of buttons like so:

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
	public override void OnInspectorGUI()
	{
		base.OnInspectorGUI();

		if (GUILayout.Button("First Button"))
		{
			Debug.Log("The first button was pressed!");
		}

		if (GUILayout.Button("Second Button"))
		{
			Debug.Log("The second button was pressed!");
		}
	}
}

If we select our object now, it will have two more buttons we can press to log some messages:

Screen Shot 2015-02-07 at 2.03.37 PM

Notice that the buttons are displayed in the order that we called GUILayout.Button(). If we called base.OnInspectorGUI() after those buttons, the buttons would be displayed at the top of the script inspector instead of the bottom.

A Slider for “Radius”

There are all kinds of things we can do with custom inspector GUIs, but let’s start with something simple. Right now our radius variable is a float field which we can manually enter in new values. Let’s change it so that radius is now a slider that slides between 0.0 and 10.0.

To do this, let’s clean up our script and get access to the radius variable on our object. We can use Editor’s target variable to access the object that this Editor is inspecting…

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
	public override void OnInspectorGUI()
	{
		base.OnInspectorGUI();

		//"target" is a reference to the selected MyScript, let's cast it to a nice variable
		MyScript scr = (MyScript)target;
	}
}

Once we have access to our MyScript object, we can use the EditorGUILayout.Slider() that modifier its radius variable, like so…

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
	public override void OnInspectorGUI()
	{
		base.OnInspectorGUI();

		MyScript scr = (MyScript)target;

		//Edit the radius value (parameters are: label, original value, min range, max range)
		scr.radius = EditorGUILayout.Slider("Radius", scr.radius, 0.0f, 10.0f);
	}
}

Once you’ve done this, your object’s inspector should look like this:

Screen Shot 2015-02-07 at 2.13.55 PM

Cool! Notice how both values move when you operate the slider? That’s neat, but we don’t really need the radius variable to be displayed there twice, so let’s add one line of code to our MyScript class…

using UnityEngine;

public class MyScript : MonoBehaviour
{
	[HideInInspector]
	public float radius;

	void OnDrawGizmos()
	{
		Gizmos.color = Color.red;
		Gizmos.DrawWireSphere(transform.position, radius);
	}
}

We can use the [HideInInspector] attribute like this to prevent the variable from being drawn in base.OnInspectorGUI().

Adding Undo Support

If you remember from the previous tutorial, after getting our menu item working, we added Undo support. Our slider currently has no Undo support, meaning if you use CTRL/Command-Z after sliding it, the results will be… confusing.

With Editor scripts we add Undo support the exact same way… the only difference is we need a little bit of code to detect when the slider has actually been moved. This is pretty straightforward:

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
	public override void OnInspectorGUI()
	{
		base.OnInspectorGUI();

		MyScript scr = (MyScript)target;

		//Get the new radius value from the slider
		float newRadius = EditorGUILayout.Slider("Radius", scr.radius, 0.0f, 10.0f);

		//Has the radius changed? If so, record undo and apply the changes
		if (scr.radius != newRadius)
		{
			Undo.RecordObject(scr, "Radius was changed");
			scr.radius = newRadius;
		}
	}
}

And last but not least, if you move the slider around, you’ll notice that the object’s wire Gizmo doesn’t change until you move it again or select something else. This is Unity trying to be resourceful by reducing refresh calls, but in this case we want the object to refresh, so we can manually do it with an additional line of code:

 

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(MyScript))]
public class MyScriptEditor : Editor
{
	public override void OnInspectorGUI()
	{
		base.OnInspectorGUI();

		MyScript scr = (MyScript)target;

		float newRadius = EditorGUILayout.Slider("Radius", scr.radius, 0.0f, 10.0f);

		if (scr.radius != newRadius)
		{
			Undo.RecordObject(scr, "Radius was changed");
			scr.radius = newRadius;

			//Tell Unity this object has changed (is "dirty") so it will refresh
			EditorUtility.SetDirty(scr);
		}
	}
}

Done! Now as you move the slider left and right, the gizmo should be resizing in real-time.

In Closing

That was a lot to cover, so this tutorial is done for now! There are many other GUI functions you can call and other editor utilities you can use, but covering them all would take a much larger tutorial, and this was only meant to warm you up to the basics.

Going forward, try playing around with different functions in the inspector and see what results you get! Good luck!

If you have questions, find any errors in this tutorial, or have a tutorial request, please contact or tweet me!

Donations

Hosting costs money and tutorials take time. Pease donate via PayPal to help keep Unity Patterns afloat! Chevy has barely any money, and every dollar is appreciated <3


Viewing all articles
Browse latest Browse all 10

Latest Images

Trending Articles





Latest Images