Unity Custom Inspectors Tutorial: Getting Started

In this tutorial you will learn how to create Custom Inspectors in the Unity Editor. From editing how your components look, to adding Editor windows :] By Gur Raunaq Singh.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Space Attribute

The HeaderAttribute helps distinguish the various categories of public fields that you might have in a script. However, if you need a little more control over how the Inspector looks, the SpaceAttribute allows you to add a vertical space between adjacent lines in the Inspector.

Add the Space Attributes after the respective line comment numbers in TankController.cs.

//6.
[Space(10)]

//9.
[Space(10)]

//15.
[Space(10)]

Save the script, and look at the Inspector.

You can see there’s slightly more of a gap between the three sections in the Inspector. It’s subtle, but it does increase the overall readability.

Tooltip Attribute

As the name suggests, the Tooltip Attribute allows you to specify a tooltip for a field in the Inspector.

Embedding additional information into public fields with the help of tooltips can remove the ambiguity that may arise when other members of the project are working on areas designed by you.

Add the Tooltip Attribute after line comment 12 in TankController.cs:

//12.
[Tooltip("Health value should be between 20 and 200. Maximum health should be based on the category of the tank.")]

When you hover the mouse pointer over Maximum Health, you’ll see a tooltip like this:

HideInInspector and SerializeField

There are situations when you have public variables in your scripts that you don’t necessarily want to display in the Inspector — you know, as part of the #CleanTheInspector campaign. :]

You might also want to see the current value of your private variables while you’re building and testing your game within the editor.

The HideInInspector attribute lets you hide public variables you’ve defined in your script.

Conversely, the SerializeField attribute lets you see private variables in your script.

Add the following attributes after the respective line comment numbers in TankController.cs.

//13.
[SerializeField]

//14.
[HideInInspector]

Save the script, and look at the Inspector.

Notice the private boolean Dead is now visible in the Inspector, and the string variable Tank Class is now hidden.

ContextMenu Attribute

You may have noticed this little icon in the Inspector at the top right of each component. It shows a small menu with some specific functions depending upon the type of component.

This handy menu is called the Context Menu. The default methods include Reset, Copy Component, Remove Component and so forth.

Suppose you’re testing how different variations of your tank health or speed affect gameplay against different types of enemies. To set a difficulty level based on your tank class, you need to set five variables to a specific value.

You’ve written the code in the TankController script, and you want an easy way to execute these methods.

In cases like these, you can use the ContextMenu attribute to add your own defined methods to the context menu of your component.

Add the ContextMenu attributes after the respective line comment numbers in TankController.cs:

//17.
[ContextMenu("Difficulty - Noob")]

//18.
[ContextMenu("Difficulty - Dark Souls")]

Save the script and take a look at the Inspector.

You now have an easy way to execute custom functions, whether it’s in editing mode or while playing a scene.

There are many more ways to customize the Inspector according to your working setup: https://docs.unity3d.com/ScriptReference/UnityEngine.CoreModule.html.

Extending the Inspector

In this section, you’ll learn how to extend the capabilities of the Inspector.

In particular, you’ll learn how to achieve the following:

  • Completely hide all items that are part of a component.
  • Create a custom form within the Inspector for setting and resetting the values of player preferences without playing the scene.
  • Add a custom button with an image as a thumbnail to execute a custom function.

Editor Scripting

The Editor folder has a special meaning in the Unity editor and the scripts contained are treated as editor scripts rather than runtime scripts.

From Assets \ RW \ Editor open TankControllerEditor.cs.

Note: A word about the naming convention of the script here. The main script is TankController, so the name of the editor script is TankControllerEditor. This makes it clear that the script is a custom editor for the TankController script.

To specify the type of component you want to edit, you use the CustomEditor attribute.

Modify the TankControllerEditor script with the following code:

using UnityEngine;
using UnityEditor;

// Declare type of Custom Editor
[CustomEditor(typeof(TankController))] //1
public class TankControllerEditor : Editor 
{
    float thumbnailWidth = 70;
    float thumbnailHeight = 70;
    float labelWidth = 150f;

    string playerName = "Player 1";
    string playerLevel = "1";
    string playerElo = "5";
    string playerScore = "100";

    // OnInspector GUI
    public override void OnInspectorGUI() //2
    {

        // Call base class method

        // Custom form for Player Preferences

        // Custom Button with Image as Thumbnail
    }
}

Looking at each piece comment-by-comment:

  1. Declare this script as a CustomEditor for the TankController class. Since you’ll be modifying the behavior of how the components of TankController look like in the Inspector. You can also specify the type of built-in components, such as Transform and Box Collider.
  2. The public override method specifies that you want to override the default behavior of the Inspector for the TankController class.

Save the file, and look at the Inspector.

What just happened?

When you call OnInspectorGUI, all of the default behavior is ignored (how or what components can be seen in the Inspector). Since there’s nothing specified in the method yet, there’s nothing to show in the Inspector.

To bring back the default behavior, add the following code after // Call base class method:

base.DrawDefaultInspector();

If you look at the Inspector, you’ll see that TankController looks the same as it did at the end of the previous section.

Custom Form

In this subsection, you’ll create a simple form to take in values from the Inspector and a button to set those values in the player preferences.

Add the following piece of code after // Custom form for Player Preferences:

// Custom form for Player Preferences
TankController tank = (TankController) target; //1

GUILayout.Space(20f); //2
GUILayout.Label("Custom Editor Elements", EditorStyles.boldLabel); //3

GUILayout.Space(10f);
GUILayout.Label("Player Preferences");

GUILayout.BeginHorizontal(); //4
GUILayout.Label("Player Name", GUILayout.Width(labelWidth)); //5
playerName = GUILayout.TextField(playerName); //6
GUILayout.EndHorizontal(); //7

GUILayout.BeginHorizontal();
GUILayout.Label("Player Level", GUILayout.Width(labelWidth));
playerLevel = GUILayout.TextField(playerLevel);
GUILayout.EndHorizontal();

GUILayout.BeginHorizontal();
GUILayout.Label("Player Elo", GUILayout.Width(labelWidth));
playerElo = GUILayout.TextField(playerElo);
GUILayout.EndHorizontal();

GUILayout.BeginHorizontal();
GUILayout.Label("Player Score", GUILayout.Width(labelWidth)); 
playerScore = GUILayout.TextField(playerScore);
GUILayout.EndHorizontal();

GUILayout.BeginHorizontal();

if (GUILayout.Button("Save")) //8
{
    PlayerPrefs.SetString("PlayerName", playerName); //9
    PlayerPrefs.SetString("PlayerLevel", playerLevel);
    PlayerPrefs.SetString("PlayerElo", playerElo);
    PlayerPrefs.SetString("PlayerScore", playerScore);

    Debug.Log("PlayerPrefs Saved");
}

if (GUILayout.Button("Reset")) //10
{
    PlayerPrefs.DeleteAll();
    Debug.Log("PlayerPrefs Reset");
}

GUILayout.EndHorizontal();

Here’s what’s happening:

  1. Get a reference to TankController. You’ll use this to reference public variables and methods defined in the class in the next section.
  2. GUILayout.Space() is used to add space in the layout group. The direction of the space is dependent on the layout when issuing the command, which is vertical by default.
  3. GUILayout.Label() is used to add a Label to the editor layout. You can style the text with a variety of options using EditorStyles. In this case, you simply have set the text to Bold.
  4. Begin a horizontal group. All elements rendered inside this layout group are placed horizontally to each other.
  5. Similar to point three. You have a Label to specify what the input would be for //6, Player Name in this case.
  6. A GUILayout.TextField is used to store text data and the result is saved in a string: playerName.
  7. End a horizontal group. Similarly three more horizontal groups are defined below to take in input for playerElo, playerLevel and playerScore.
  8. Makes a single press button. The if block acts as a block of code that’s executed whenever the button is clicked.
  9. The value of variable playerName is saved as value of string playerName in player preferences. Similarly the value of playerElo, playerLevel and playerScore.
  10. Similarly, a reset button is added to delete values in player preferences.

Save the script, select Tank from the Hierarchy and look at the Inspector.

Now you have a cool custom form that you can use to set or reset values in the player preferences easily and without necessarily going into play mode.

Gur Raunaq Singh

Contributors

Gur Raunaq Singh

Author and Author

Ben MacKinnon

Tech Editor

Luke Freeman

Illustrator

Sean Duffy

Final Pass Editor

Over 300 content creators. Join our team.