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))]3public class Car_Inspector : Editor4{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 VisualElement3/// </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>7public 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 field27 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 VisualElement3/// </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>7public 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 field18 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:
1private 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}