Editor for instance properties

I have a IsoTile class and a terrain property of class TerrainType. The IsoTile is designed to be added as a component to a lot of game objects on the scene and be configurable using standard Unity Inspector window.

public class IsoTile : MonoBehaviour {
    public TerrainType terrain;
    // ...
}

public abstract class TerrainType : ScriptableObject {
}

public class SimpleTerrain : TerrainType {
}

public class ComplexTerrain : TerrainType {
}

public class TransitionTerrain : TerrainType {
}

Here is what I see in editor, but I want to choose between all my terrain types. How could I do that?

24548-2014-03-31_2012.png

If what you need is a drop-down showing SimpleTerrain, ComplexTerrain and TransitionTerrain you will need to write your own property drawer or custom editor.

Here’s a code I wrote some time ago to achieve that:

TypeSelectorAttribute.cs

[AttributeUsage (AttributeTargets.Field)]
public class TypeSelectorAttribute : PropertyAttribute {

	public Type type { get; private set; }
	
	public TypeSelectorAttribute (Type type) : this (type, false) {
	}
	
	public TypeSelectorAttribute (Type type, bool runtimeReadOnly) {
		this.type = type;
	}

}

TypeSelectorAttributeDrawer.cs

[CustomPropertyDrawer (typeof (TypeSelectorAttribute))]
public class TypeSelectorDrawer : PropertyDrawer {

	static Dictionary<System.Type, List<string>> types = new Dictionary<System.Type, List<string>> ();

	void CollectTypes (System.Type type) {
		List<string> subtypes = System.AppDomain.CurrentDomain.GetAssemblies ().SelectMany (asm => asm.GetTypes ()).Where (t => t.IsSubclassOf (type) && !t.IsAbstract).Select (t => t.Name).ToList ();
		types.Add (type, subtypes);
	}

	public override float GetPropertyHeight (UnityEditor.SerializedProperty property, GUIContent label) {
		return 18f;
	}

	public override void OnGUI (Rect position, UnityEditor.SerializedProperty property, GUIContent label) {

		position.yMin += 2f;

		TypeSelectorAttribute attr = attribute as TypeSelectorAttribute;
		System.Type type = attr.type;
		
		if (!types.ContainsKey (type))
			CollectTypes (type);
		
		int idx = types[type].ToList ().IndexOf (property.stringValue);

		if (idx == -1)
			idx = 0;

		if (types[type].Count > idx) {
			EditorGUI.BeginProperty (position, label, property);
			EditorGUI.BeginChangeCheck ();
			idx = EditorGUI.Popup (position, label.text, idx, types[type].ToArray ());
			if (EditorGUI.EndChangeCheck ()) {
				property.stringValue = types[type][idx];
			}
			
			if (property.stringValue == string.Empty)
				property.stringValue = types[type][idx];

			EditorGUI.EndProperty ();
		} else {
			EditorGUI.LabelField (position, label.text, "No " + type.Name + "s found");
		}
	}

}

Usage:

[TypeSelector (typeof (TerrainType))]
public TerrainType terrain;

(Bear in mind that I copy-pasted code and stripped some useless parts of it, just to demonstrate here real code on how you would achieve that).