Note: This post will be regularly updated with other examples. Make sure to return later. Last edit: 31/Jan/2015
There are a few official tutorials [1][2] and videos about this subject, and others made by community. The thing is, the manual itself doesn’t cover a lot of things, it uses mostly UnityScript, and many other things lacks of examples. In this post, I’ll try to keep it always updated with my last codes.
First, lets start with the basics, and I assume you know Unity3D and C# well. The default Unity inspector have a few properties bars built-in, and sometimes it’s not enough. The not-so-new Unity 4 introduced Property Drawers. If you have an unique property in diferent objects, then that’s what you need. Let’s take a look on how default built-in properties works.
My Enemy class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
using UnityEngine; using System.Collections; public class Enemy : MonoBehaviour { [Range (0, 100)] public int Health; [Range (0, 250)] public float Mana; [Multiline] public string EnemyAttackDescription; } |
That gives me the following inspector:
Now let’s add a “power attack” to our enemy. It will be a simple Serializable Class. Check the following code:
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 |
using UnityEngine; using System.Collections; public class Enemy : MonoBehaviour { [Range (0, 100)] public int Health; [Range (0, 250)] public float Mana; [Multiline] public string EnemyAttackDescription; public SpecialAttack Special; } [System.Serializable] public class SpecialAttack { public int Level; public Special Type; } public enum Special { Range, Melee, Magic } |
Before talking about Property Drawers, you must know that you can create a custom PropertyAttribute, just like Attributes in C#. Range and Multiline are built-in, but you can create your own, and combine with Property Drawers. As far as I can tell, PropertyAttribute’s can only deal with one property. Property Drawers can combine PropertyAttribute, and/or other properties for a better display.
So, I added a struck and an enum type. The special attack its level-type property. Lets say the enemy will attack with Melee until level 20, Range between 21 and 30, and Magic from level 31. So, let’s create our SpecialAttack Custom Property Drawer
SpecialDrawer.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
using UnityEngine; using UnityEditor; using System.Collections; [CustomPropertyDrawer (typeof (SpecialAttack))] class SpecialDrawer : PropertyDrawer { public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { // Storing original Rect position var rectPos = position; rectPos.width -=200; // Removing "200" from the original width of all Rect. EditorGUI.PropertyField (rectPos, property.FindPropertyRelative("Level"), new GUIContent() { text = "Level/Type" }); rectPos.x = rectPos.width + 20; // My next field will be at the same Y position but to the right rectPos.width = position.width - rectPos.width - 10; // Eye adjustment.. EditorGUI.PropertyField (rectPos, property.FindPropertyRelative("Type"), GUIContent.none ); } } |
Explaining: I store the original Rect position given by my PropertyDrawer, and I do some modifications with the axis X and width. This way, I can display the 2 properties in one line. The EditorGUI.PropertyField can guess what type of variable it is, thats why it renders Popups with Enum, TextField’s for strings, and so on. You can force it to draw an IntSlider instead of a simple textfield. We will talk about this in another topic.
Ok, what about forcing Type according to Level value? I’m just doing that to demonstrate how we can manipulate values inside a Property Drawer. If it was real case, I would probably use some Label to display the Type, because changing it would have no value because its linked to level value. Anyway, here’s an updated code:
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 32 33 34 35 |
using UnityEngine; using UnityEditor; using System.Collections; [CustomPropertyDrawer (typeof (SpecialAttack))] class SpecialDrawer : PropertyDrawer { public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { var rectPos = position; var propLevel = property.FindPropertyRelative ("Level"); // Separating each property var propType = property.FindPropertyRelative ("Type"); // Separating each property rectPos.width -=200; EditorGUI.PropertyField (rectPos, propLevel, new GUIContent() { text = "Level/Type" }); rectPos.x = rectPos.width + 20; rectPos.width = position.width - rectPos.width - 10; EditorGUI.PropertyField (rectPos, propType, GUIContent.none ); // Forcing range between 1 and 50 if (propLevel.intValue <= 0) propLevel.intValue = 1; else if (propLevel.intValue > 50) propLevel.intValue = 50; // Dirty code to change Popup value if(propLevel.intValue > 0 && propLevel.intValue < 20) { propType.enumValueIndex = (int)Special.Melee; } else if(propLevel.intValue >= 20 && propLevel.intValue < 30 ) { propType.enumValueIndex = (int)Special.Range; } else if(propLevel.intValue >= 30) { propType.enumValueIndex = (int)Special.Magic; } } } |
Also I am forcing the Level values between 1 and 50. I could also do that using [Range(1,50)] inside SpecialAttack class, like this:
1 2 3 4 5 6 7 8 |
[System.Serializable] public class SpecialAttack : PropertyAttribute { [Range(1,50)] public int Level; public Special Type; } |
Then I am forcing the Type value using simple if checks. Hope it helps… use the comment section!