Skip to content

Custom Inspector

Intro

Custom Inspectors are a necessity when you want to boost productivity for your components and Scriptable Objects

Basics

Reading the documentation gives you the basics. But I’m going to give you some additional tips and tricks.

If you want to display the default inspector inside a custom inspector then using the FillDefaultInspector is great:

1
// Attach a default Inspector to the Foldout.
2
[CustomEditor(typeof(Car))]
3
public class Car_Inspector : Editor
4
{
5
public VisualTreeAsset m_InspectorXML;
6
public override VisualElement CreateInspectorGUI()
7
{
8
// Create a new VisualElement to be the root of the Inspector UI.
9
VisualElement myInspector = new VisualElement();
10
11
// Load from default reference.
12
m_InspectorXML.CloneTree(myInspector);
13
14
// Get a reference to the default Inspector Foldout control.
15
VisualElement InspectorFoldout = myInspector.Q("Default_Inspector");
16
17
// Attach a default Inspector to the Foldout.
18
InspectorElement.FillDefaultInspector(InspectorFoldout, serializedObject, this);
19
20
// Return the finished Inspector UI.
21
return myInspector;
22
}
23
}

But in some cases you may wish to only display the default inspector for most fields, but not all of them, such that you can write custom visual elements for those. In that case I usually iterate through the serialized object iterator to get all serialized properties. And just have a list of fields to ignore.

1
/// <summary>
2
/// Adds default inspector property fields under a container VisualElement
3
/// </summary>
4
/// <param name="ignoreProperties">A list of names of properties to ignore</param>
5
/// <param name="container">The parent VisualElement</param>
6
/// <param name="serializedObject">The SerializedObject to inspect</param>
7
public static void FillDefaultInspector(List<string> ignoreProperties, VisualElement container, SerializedObject serializedObject)
8
{
9
if (serializedObject == null) {
10
return;
11
}
12
13
// Get the iterator to loop through all serialized properties.
14
SerializedProperty iterator = serializedObject.GetIterator();
15
if (iterator.NextVisible(true) == false) {
16
return;
17
}
18
19
// Go through all the serialized properties.
20
do {
21
//ignore the list of ignored fields.
22
if (ignoreProperties.Contains(iterator.name)) {
23
continue;
24
}
25
26
// Create the field
27
PropertyField propertyField = new PropertyField(iterator);
28
propertyField.name = "PropertyField:" + iterator.propertyPath;
29
30
// Disable the script property so that it cannot be changed.
31
if (iterator.propertyPath == "m_Script") {
32
propertyField.SetEnabled(false);
33
}
34
35
// Add the field to the container.
36
container.Add(propertyField);
37
38
} while (iterator.NextVisible(false));
39
}

On the other hand if you wish to draw a list of properties then you can do so like this:

1
/// <summary>
2
/// Adds the list of property names as property fields under a container VisualElement
3
/// </summary>
4
/// <param name="propertyNames">A list of names of properties to add</param>
5
/// <param name="container">The parent VisualElement</param>
6
/// <param name="serializedObject">The SerializedObject to inspect</param>
7
public static void AddSerializedPropertyFields(List<string> propertyNames, VisualElement container, SerializedObject serializedObject)
8
{
9
if (serializedObject == null) {
10
return;
11
}
12
13
for (int i = 0; i < propertyNames.Count; i++) {
14
var property = serializedObject.FindProperty(propertyNames[i]);
15
if(property == null){ continue; }
16
17
// Create the field
18
PropertyField propertyField = new PropertyField(property);
19
propertyField.name = "PropertyField:" + property.propertyPath;
20
21
// Add the field to the container.
22
container.Add(propertyField);
23
}
24
}

With those two functions you can pretty much add all the default fields you want. And you can now start writing your custom fields.

When changing values manually, don’t forget to add an undo and dirty to object:

1
private void ChangePropertyValueManuallyExample()
2
{
3
var thisObject = target;
4
5
Undo.RecordObject(thisObject, "Undo Change Name for debugging");
6
7
//Do your manual changes here.
8
9
EditorUtility.SetDirty(thisObject);
10
PrefabUtility.RecordPrefabInstancePropertyModifications(thisObject);
11
EditorSceneManager.SaveScene(SceneManager.GetActiveScene());
12
}