How to Create a Custom Inspector in the Unity Engine

Sometimes you need to customize the Inspector UI in the Unity engine to better suit your objects’ properties. You may want for example to modify your object with a click of a button or change its attributes from the inspector without modifying the code. In this blog, we will show you step-by-step how to do that. Note, we’re using Unity version 6000.0.51f1 which uses the new method of creating custom inspectors, so this code may not work for older versions.
Create the Target Object’s Class
First we need to create the class of the object that we wish to modify from the inspector. This could be anything, for example, a car class, a character class and so on. In this blog, we will create a GameLevel class. This class will represent a level in a game. The custom inspector will allow us to add objects to the level with a click of a button, and also set their attributes such as position, name, color and more. So instead of dragging prefabs from the project folders to the scene, you simply click a button. Think of this exercise as making a very simplistic level editor.
Create a new C# script by right-clicking the Assets folder and selecting Create -> Scripting -> MonoBehaviour Script. Name the script GameLevel. Paste this code inside the GameLevel class:
using UnityEngine;
public class GameLevel : MonoBehaviour
{
[SerializeField]
private GameObject cubePrefab;
[SerializeField]
private GameObject spherePrefab;
public void AddCube(string name, Vector3 position, Vector3 rotation, Vector3 scale)
{
Quaternion quat = Quaternion.Euler(rotation);
GameObject gameObject = Instantiate(cubePrefab, position, quat);
gameObject.transform.localScale = scale;
gameObject.name = name;
}
public void AddSphere(string name, Vector3 position, float radius)
{
GameObject gameObject = Instantiate(spherePrefab, position, Quaternion.identity);
gameObject.transform.localScale = new Vector3(radius, radius, radius);
gameObject.name = name;
}
}
This class has two members: cubePrefab and spherePrefab. They represent the prefabs of a cube and a sphere respectively that we will create in a moment and assign them to these members. The class also has two functions: AddCube and AddSphere. These functions are similar, they both create an object, a cube or a sphere. AddCube takes three parameters: position, rotation and scale. AddSphere takes two parameters: position and radius. These parameters will be set in the inspector before creating the object, allowing you to place, rotate and scale the object as you wish.
Create the Editor’s Class
Now we will create the class responsible for making the custom inspector. This inspector will contain for now the following fields:
- Position: Allows to set the position of the object.
- Rotation: Allows to set the rotation of the object.
- Scale: Allows to set the scale of the object.
- Radius: Allows to set the radius of the sphere object. This field will not affect the cube.
- Name: Allows to set the name of the object.
- Cube Prefab: Allows to attach the cube prefab.
- Sphere Prefab: Allows to attach the sphere prefab.
- Create Cube button: Button to add a cube to the scene.
- Create Sphere button: Button to add a cube to the scene.
Create a new empty C# script by right-clicking the Assets folder and selecting Create -> Scripting -> Empty C# Script and name it LevelEditor. Paste in it the following code:
using UnityEditor;
using UnityEngine.UIElements;
using UnityEngine;
using UnityEditor.UIElements;
[CustomEditor(typeof(GameLevel))]
public class LevelEditor : Editor
{
private Vector3 position = Vector3.zero;
private Vector3 rotation = Vector3.zero;
private Vector3 scale = Vector3.one;
private float radius = 1.0f;
private string name = "Default";
public override VisualElement CreateInspectorGUI()
{
GameLevel levelEditor = (GameLevel)target;
VisualElement myInspector = new VisualElement();
myInspector.Add(new Label("Custom Level Editor"));
Vector3Field positionField = new Vector3Field("Position");
positionField.value = position;
positionField.RegisterValueChangedCallback(evt =>
{
position = evt.newValue;
});
myInspector.Add(positionField);
Vector3Field rotationField = new Vector3Field("Rotation");
rotationField.value = rotation;
rotationField.RegisterValueChangedCallback(evt =>
{
rotation = evt.newValue;
});
myInspector.Add(rotationField);
Vector3Field scaleField = new Vector3Field("Scale");
scaleField.value = scale;
scaleField.RegisterValueChangedCallback(evt =>
{
scale = evt.newValue;
});
myInspector.Add(scaleField);
FloatField radiusField = new FloatField("Radius");
radiusField.value = radius;
radiusField.RegisterValueChangedCallback(evt =>
{
radius = Mathf.Max(1.0f, evt.newValue);
});
myInspector.Add(radiusField);
TextField textField = new TextField("Name");
textField.value = name;
textField.RegisterValueChangedCallback(evt =>
{
name = evt.newValue.Length > 0 ? evt.newValue : "Default";
});
myInspector.Add(textField);
SerializedProperty cubePrefabProperty = serializedObject.FindProperty("cubePrefab");
PropertyField cubeObjectField = new PropertyField(cubePrefabProperty);
cubeObjectField.label = "Cube Prefab";
myInspector.Add(cubeObjectField);
SerializedProperty spherePrefabProperty = serializedObject.FindProperty("spherePrefab");
PropertyField sphereObjectField = new PropertyField(spherePrefabProperty);
sphereObjectField.label = "Sphere Prefab";
myInspector.Add(sphereObjectField);
Button addCubeButton = new Button(() =>
{
levelEditor.AddCube(name, position, rotation, scale);
})
{
text = "Create Cube"
};
Button addSphereButton = new Button(() =>
{
levelEditor.AddSphere(name, position, radius);
})
{
text = "Create Sphere"
};
myInspector.Add(addCubeButton);
myInspector.Add(addSphereButton);
return myInspector;
}
}
Let’s break down the code. The line above the class declaration tells the Unity engine that this class is a custom editor for the GameLevel class. Without this the custom inspector will not be displayed.
[CustomEditor(typeof(GameLevel))]
The five member variables all store the information that you input from the inspector. Here we initialize them with default values.
private Vector3 position = Vector3.zero;
private Vector3 rotation = Vector3.one;
private Vector3 scale = Vector3.one;
private float radius = 1.0f;
private string name = "Default";
Now we get to the CreateInspectorGUI() function. This function houses all your code which defines the inspector’s UI elements. This function comes from the Editor class which we inherit from, and you have to override this function. It returns a VisualElement object that represents your UI layout. target is a reference to the object being inspected in the custom editor, which in our case is the object of class GameLevel. target comes from the Editor class so you don’t need to define it yourself.
GameLevel levelEditor = (GameLevel)target;
From there, it’s pretty simple. You just need to create the UI elements and add them to the VisualElement object. Here’s a sample below where we added the Position field. We used a class named Vector3Field. This adds to the inspector three text fields for the x,y, and z components of the vector. The RegisterValueChangedCallback is triggered every time you change a value of the x,y or z fields in the inspector. Then inside the body of that function we save the vector value to our member variable.
Vector3Field positionField = new Vector3Field("Position");
positionField.value = position;
positionField.RegisterValueChangedCallback(evt =>
{
position = evt.newValue;
});
myInspector.Add(positionField);
To be able to attach our prefabs, we use the field type PropertyField.
SerializedProperty cubePrefabProperty = serializedObject.FindProperty("cubePrefab");
PropertyField cubeObjectField = new PropertyField(cubePrefabProperty);
cubeObjectField.label = "Cube Prefab";
myInspector.Add(cubeObjectField);
Now let’s have a look at the buttons we created. It’s very similar to the code we explained above for the Position vector fields, however, this time we’re using the Button object instead of the Vector3Field. Similarly, the code that is triggered when the button is clicked is inside the body of the Button declaration. Here, we call the AddCube function that we defined earlier.
Button addCubeButton = new Button(() =>
{
levelEditor.AddCube(name, position, rotation, scale);
})
{
text = "Create Cube"
};
Finally, we return the inspector object.
return myInspector;
Testing the Custom Editor
Now let’s go back to the Unity engine and see how this all works. Create a new Empty game object by right-clicking the Hierarchy and selecting Create Empty and name it Level.
Now attach the GameLevel script to the Level object by dragging the GameLevel script from the Assets folder and dropping it in the inspector of Level. If you select it in the Hierarchy, you should now see the custom inspector that we created for this object.
Let’s create the prefabs. Create a cube by right-clicking the Hierarchy and selecting 3D Object -> Cube. Rename it to Cube, then reset the transform by clicking the three dots in the Inspector next to Transform and click Reset. This will reset the position, rotation and scale.
Now, drag the Cube object from the Hierarchy and drop it in the Assets folder to make a prefab. You can now delete the Cube object in the Hierarchy.
We do the same thing for the sphere. Create a sphere by right-clicking the Hierarchy and selecting 3D Object -> Sphere. Rename it to Sphere, then reset the transform as we did for the cube.
Now, drag the Sphere object from the Hierarchy and drop it in the Assets folder to make a prefab as we did for the cube. You can now delete the Sphere object in the Hierarchy.
Select the Level object, then drag the Cube prefab from the Assets folder and drop it in the Cube Prefab field in the inspector. For the sphere, drag the Sphere prefab from the Assets folder and drop it in the Sphere Prefab field in the inspector.
Now, if you click the Create Cube button, it should create a cube using the default values. Same thing for the Create Sphere button.
Now, let’s change the default values. Set the Position to 10, 0, 0, the Rotation to 0, 45, 0 and the Scale to 2, 2, 2. Seth the Name to New Cube. Now click Create Cube. Notice that the cube is now 10 units away from the origin in the x axis and rotated 45 degrees around the y axis and the scale is larger.
Try the Create Sphere button, play around with the Radius value and see the results.
For our final exercise, let’s do something a bit more interesting. Let’s set the color of the objects. Open back the code, and in the GameLevel class, add the Color parameter and the code to get the material from the object and set its color as highlighted below.
using UnityEngine;
public class GameLevel : MonoBehaviour
{
[SerializeField]
private GameObject cubePrefab;
[SerializeField]
private GameObject spherePrefab;
public void AddCube(string name, Vector3 position, Vector3 rotation, Vector3 scale, Color color)
{
Quaternion quat = Quaternion.Euler(rotation);
GameObject gameObject = Instantiate(cubePrefab, position, quat);
gameObject.transform.localScale = scale;
gameObject.name = name;
MeshRenderer meshRenderer = gameObject.GetComponent<MeshRenderer>();
if (meshRenderer && meshRenderer.sharedMaterial)
{
Material original = meshRenderer.sharedMaterial;
Material materialClone = new Material(original);
materialClone.color = color;
meshRenderer.material = materialClone;
}
}
public void AddSphere(string name, Vector3 position, float radius, Color color)
{
GameObject gameObject = Instantiate(spherePrefab, position, Quaternion.identity);
gameObject.transform.localScale = new Vector3(radius, radius, radius);
gameObject.name = name;
MeshRenderer meshRenderer = gameObject.GetComponent<MeshRenderer>();
if (meshRenderer && meshRenderer.sharedMaterial)
{
Material original = meshRenderer.sharedMaterial;
Material materialClone = new Material(original);
materialClone.color = color;
meshRenderer.material = materialClone;
}
}
}
Now open the LevelEditor code and add the color related changes.
using UnityEditor;
using UnityEngine.UIElements;
using UnityEngine;
using UnityEditor.UIElements;
[CustomEditor(typeof(GameLevel))]
public class LevelEditor : Editor
{
private Vector3 position = Vector3.zero;
private Vector3 rotation = Vector3.zero;
private Vector3 scale = Vector3.one;
private float radius = 1.0f;
private string name = "Default";
private Color color = Color.white;
public override VisualElement CreateInspectorGUI()
{
GameLevel levelEditor = (GameLevel)target;
VisualElement myInspector = new VisualElement();
myInspector.Add(new Label("Custom Level Editor"));
Vector3Field positionField = new Vector3Field("Position");
positionField.value = position;
positionField.RegisterValueChangedCallback(evt =>
{
position = evt.newValue;
});
myInspector.Add(positionField);
Vector3Field rotationField = new Vector3Field("Rotation");
rotationField.value = rotation;
rotationField.RegisterValueChangedCallback(evt =>
{
rotation = evt.newValue;
});
myInspector.Add(rotationField);
Vector3Field scaleField = new Vector3Field("Scale");
scaleField.value = scale;
scaleField.RegisterValueChangedCallback(evt =>
{
scale = evt.newValue;
});
myInspector.Add(scaleField);
FloatField radiusField = new FloatField("Radius");
radiusField.value = radius;
radiusField.RegisterValueChangedCallback(evt =>
{
radius = Mathf.Max(1.0f, evt.newValue);
});
myInspector.Add(radiusField);
TextField textField = new TextField("Name");
textField.value = name;
textField.RegisterValueChangedCallback(evt =>
{
name = evt.newValue.Length > 0 ? evt.newValue : "Default";
});
myInspector.Add(textField);
ColorField colorField = new ColorField("Color");
colorField.value = color;
colorField.RegisterValueChangedCallback(evt =>
{
color = evt.newValue;
});
myInspector.Add(colorField);
SerializedProperty cubePrefabProperty = serializedObject.FindProperty("cubePrefab");
PropertyField cubeObjectField = new PropertyField(cubePrefabProperty);
cubeObjectField.label = "Cube Prefab";
myInspector.Add(cubeObjectField);
SerializedProperty spherePrefabProperty = serializedObject.FindProperty("spherePrefab");
PropertyField sphereObjectField = new PropertyField(spherePrefabProperty);
sphereObjectField.label = "Sphere Prefab";
myInspector.Add(sphereObjectField);
Button addCubeButton = new Button(() =>
{
levelEditor.AddCube(name, position, rotation, scale, color);
})
{
text = "Create Cube"
};
Button addSphereButton = new Button(() =>
{
levelEditor.AddSphere(name, position, radius, color);
})
{
text = "Create Sphere"
};
myInspector.Add(addCubeButton);
myInspector.Add(addSphereButton);
return myInspector;
}
}
Now go back to the Unity engine, select the Level object. You should see the new color selector.
Select the color you like, we choose red, then click the Create Cube or Create Sphere button. It should now change the color of the created objects.
Use the UI Builder to Create the Custom Editor
If you don’t like using code to create the UI, you can do it from the Unity UI by using the UI Builder. We won’t cover this in this blog, but you can find a guide here from Unity’s website here.
We hope you found this blog useful. If you’re interested, please check out our apps and Unity assets below.
We Need Your Help!
Please check out our apps and games, we also have a few Unity icon packs in the Unity Asset Store if you need them for your game or app. Your support is very much appreciated!
You Might Also Like
- How to Create a Custom Inspector in the Unity Engine.
- Introduction to Reactive Programming in Android.
- Wooden Icons Pack Available in the Unity Asset Store.
- Glossy Icons Pack Available in the Unity Asset Store.
- Flat Icons Pack Available in the Unity Asset Store.
- How to Implement In-App Purchases in an Android App.
- Note Chain Available Now!
- Tips for Developing a Game as a Solo Developer.
- How to Make a 2D Rain Effect in the Unity Engine.
- Tips for Debugging Unity Games.
- How to Improve the Performance of Your Unity Game.
- Thirsty Plant Available Now!
- How to Implement In-App Purchases with Unity IAP.
- How to Create a Dripping Liquid Effect in the Unity Engine.