Custom Editors in Unity3D – Part 6: SerializedObject, SerializedProperty, PropertyDrawer

Welcome back to my series on writing custom editors in Unity3D.  In this post, we’ll finally write our first custom editor – a PropertyDrawer.  First though, we’ll need to take a quick look at two more helper classes we can use: SerializedObject and SerializedProperty.

SerializedObject and SerializedProperty

The last things I want to cover briefly before we get into our first custom editor are the SerializedObject and SerializedProperty classes.  Essentially, these two classes provide a wrapper that you can use when editing your data which automatically handles certain editor-related tasks for you, such as Undo/Redo, Multi-Object Editing, and informing your object that its data has changed (i.e. when you change a field on an instance of a prefab and it becomes bold).  In general, if you can use SerializedObject and SerializedProperty, it is recommended to do so because of the benefits they provide, but if you find it easier to manipulate the data directly (in some cases it can be hard to get SerializedObject/Property to work the way you want) it’s not the end of the world.

Creating a SerializedObject is simple, you simply call new SerializedObject(…), passing in an object derived from UnityEngine.Object or an array of objects derived from UnityEngine.Object (for multi-object editing).  From there, you can use the .FindProperty(string name) method to get specific SerializedProperty objects for fields on the object (name in this case is the actual name of the variable).

Important Note: When using SerializedObject, be sure to call [your serialized object].ApplyModifiedProperties() once you’re done making changes to actually save them

Working with SerializedProperties is fairly simple – here are some of the properties/functions you’ll primarily be working with:

property.propertyType – Used to determine the underlying type of the property (returns a SerializedPropertyType, not an actual type object).  You can use this to conditionally edit a property (i.e. if you wanted to handle ints and floats with similar code, but some slight difference)

property.floatValue – Used to get/set the float value of the property (use intValue for ints, stringValue for strings, etc)

property.FindPropertyRelative(string name) – If the SerializedProperty is an object, this can be used to get properties on that object (used just like FindProperty on SerializedObject).  Here’s a brief example of FindProperty and FindPropertyRelative:

[gist https://gist.github.com/ryanmeier/e93814f343cc6bae503c]

One final note on SerializedProperties is that EditorGUI and EditorGUILayout both provide a PropertyField function, which takes a SerializedProperty, and will automatically draw the right editor and apply the modified values as appropriate (generally equivalent to what the default editor would do for that field type).

PropertyDrawer

Alright, now let’s put all of these tools we’ve learned about to use and write our first custom editor!  The first type of custom editor we’re going to look at is a PropertyDrawer.  A PropertyDrawer is essentially a way to specify how the inspector should display and edit specific fields – either based on their type (if the field is a Serializable Class), or based on a PropertyAttribute applied to the field (which we’ll get into next time).  In this case, we’re going to look at how to make a PropertyDrawer for a custom serializable class.  In order to write a custom PropertyDrawer, you’ll need to do the following:

1) Create a new class in a like-named .cs file below an ‘Editor’ folder somewhere in your file structure.

2) Make your new class extend the PropertyDrawer class, and tag it with the [CustomPropertyDrawer(type)] Attribute, where type is the type of object you’d like this drawer to edit (see below for an example of this)

3) Override the OnGUI(Rect position, SerializedProperty property, GUIContent label) function (this is where your editor code goes)

4 (optional) Override the float GetPropertyHeight(SerializedProperty property, GUIContent label) function if your editor code’s resulting height is different than a ‘standard’ field for your type (in most cases, standard = one line)

As an example, let’s look at a simple serializable class, BoolVector3 (essentially a Vector3, but instead of 3 floats, it’s 3 bools):

[gist https://gist.github.com/ryanmeier/7794eef472c875c25805]

And the default inspector that shows up if we put this on a MonoBehavior:

BoolVector3NoDrawer

This is usable, but kind of space-inefficient and not super elegant, I’d really love to have this look more like a standard Vector3.  Enter BoolVector3Drawer.cs!

[gist https://gist.github.com/ryanmeier/6562c5e9974f5e00c9f3]

And the result:

BoolVector3Drawer

If you’ve been following along, most of that code should hopefully make sense (all the fun code is in OnGUI).  We get passed a SerializedProperty representing the property being edited (in this case, since we specified it with the CustomPropertyDrawer attribute – a BoolVector3), and from there grab the x, y, and z properties.  Then for each of the three, we draw a label, and use a boolField to get the updated value from the editor.

Note: PropertyDrawers are one of the few times I don’t normally use EditorGUILayout – because it doesn’t work in all cases by default.  There is a workaround for this, but it somewhat defeats the purpose of PropertyDrawers in the first place, so I recommend just using EditorGUI.

And that about wraps up PropertyDrawers.  Next time we’ll take a look at how to write our own PropertyAttributes (which work in combination with PropertyDrawers) to add custom displays and logic to specific fields in our classes.

Thanks for reading!  If you have any questions, comments, or suggestions about anything in this post or other things you’d like to see covered, feel free to leave them in the comments below!

 

One thought on “Custom Editors in Unity3D – Part 6: SerializedObject, SerializedProperty, PropertyDrawer

Leave a Reply

Your email address will not be published. Required fields are marked *