One of the best things about working in Unity is how easy it is to customise the editor, and there’s some (relatively) helpful documentation around creating a custom inspector or a custom PropertyDrawer. But recently I had to make a custom inspector show a custom PropertyDrawer and it took some time to piece together the information needed to do that so I thought I’d quickly write it up.
The secret to all this is the PropertyField, which is similar to the IntField or FloatField but it will draw whatever type of field is the default for the type of the property, which will be the custom PropertyDrawer if the type has one.
The tricky thing about using the PropertyField is that you need to pass it a SerializedProperty. Working with a SerializedProperty is pretty easy but there’s a few steps and it’s easy to miss some of them.
To start out, let’s create the type we want to make a custom PropertyDrawer for:
1 2 3 4 5 | [Serializable] public class MyProperty { public int Prime; } |
We’ve created a type with a single field, which is supposed to contain a prime number. We want to make it so that in the Unity editor, you can only enter a prime number. So we’ll make a custom PropertyDrawer like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | [CustomPropertyDrawer(typeof(MyProperty))] public class MyPropertyDrawer : PropertyDrawer { private readonly int[] primes = {2, 3, 5, 7, 11, 13, 17, 19}; public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { // Need to wrap this is Begin/End Property so prefab override logic works (so says the docs: http://docs.unity3d.com/ScriptReference/PropertyDrawer.html) EditorGUI.BeginProperty(position, label, property); // Draw a nice prefix label position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), new GUIContent("Prime")); // Create the rect for our field var rect = new Rect(position.x, position.y, position.width, position.height); // The property we want to edit is the 'Prime' field on the 'MyProperty' type. The // property passed into this method is an instance of the 'MyProperty' type on some // serialized object (e.g. a GameObject) var primeProperty = property.FindPropertyRelative("Prime"); // Draw a popup to select a prime var newValue = EditorGUI.IntPopup(rect, primeProperty.intValue, primes.Select(prime => prime.ToString()).ToArray(), primes); // If the value has changed, apply it. This allows undo to work if (newValue != primeProperty.intValue) { primeProperty.intValue = newValue; primeProperty.serializedObject.ApplyModifiedProperties(); } EditorGUI.EndProperty(); } } |
So far so good, now we’ll make a MonoBehaviour that has a field of our MyProperty type:
1 2 3 4 | public class SomeBehaviour : MonoBehaviour { public MyProperty MyProperty; } |
Now, if we wanted to make a custom inspector for the SomeBehaviour type, we can do this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | [CustomEditor(typeof(SomeBehaviour))] public class SomeBehaviourEditor : Editor { private SerializedProperty property; void OnEnable() { property = serializedObject.FindProperty("MyProperty"); } public override void OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.PropertyField(property); } } |
Here serializedObject is the object being edited (in this case an instance of the SomeBehaviour type.
That’s it, the custom inspector will now draw using the custom PropertyDrawere we created.