I've written some code that allows Unity3D's inspector to display fields that conform to an interface. Unity has some quirks about their inspector, so as a preface they are listed here:
- If you add a
[Serializable]
attribute to a class, Unity's Inspector will attempt to show all public fields inside that class. - Any class extending
Monobehaviour
automatically has the[Serializable]
attribute - Unity's inspector will attempt to display any private field with the
[SerializeField]
attribute. - Unity's inspector will not attempt to display generic types or interfaces, with the exception of
List<T>
, which is hard-coded. - Unity's inspector will not attempt to display properties. A common workaround is to have a private backing field for your property with
[SerializeField]
attached. Setters won't be called on the value set in the inspector. It's typically only set pre-compilation time, although a developer can modify values in the inspector during runtime. Currently it is acceptable to me to only take the initial value, although if anybody has a simple and efficient way to update successfully, I'd be happy to hear it. - Unity has a
PropertyDrawer
class you can extend to control how a type is displayed in the inspector. ThePropertyDrawer
for an interface or generic type will be ignored.
The code
UnityInterfaceHelper.cs
[Serializable]
public class UnityInterfaceHelperBase
{
[Tooltip("The component that is of the type required.")]
[SerializeField]
public Component target;
}
[Serializable]
public class UnityInterfaceHelper<TInterface> where TInterface : class
{
public TInterface TargetAsInterface
{
get
{
if (targetAsInterface == null)
{
targetAsInterface = target as TInterface;
}
return targetAsInterface;
}
set
{
if (targetAsInterface != value)
{
targetAsInterface = value;
if (value as Component != null)
{
target = targetAsInterface as Component;
}
}
}
}
[Tooltip("The component that is of the type required.")]
[SerializeField]
private Component target;
private TInterface targetAsInterface;
public static implicit operator UnityInterfaceHelper<TInterface>(UnityInterfaceHelperBase b)
{
return new UnityInterfaceHelper<TInterface>()
{
target = b.target
};
}
public static implicit operator UnityInterfaceHelperBase(UnityInterfaceHelper<TInterface> b)
{
return new UnityInterfaceHelperBase()
{
target = b.target
};
}
}
UnityInterfaceHelperPropertyDrawer.cs
[CustomPropertyDrawer(typeof(UnityInterfaceHelperBase), true)]
public class UnityInterfaceHelperPropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
label = EditorGUI.BeginProperty(position, label, property);
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
EditorGUI.PropertyField(position, property.FindPropertyRelative("target"), GUIContent.none);
EditorGUI.EndProperty();
}
}
Typical usage
[SerializeField]
private UnityInterfaceHelperBase itemComparable;
public IComparable ItemComparable
{
get { return ((UnityInterfaceHelper<IComparable>)itemComparable).TargetAsInterface; }
set { ((UnityInterfaceHelper<IComparable>)landedComparable).TargetAsInterface = value; }
}
public void CompareItems()
{
if(ItemComparable.CompareTo("Hello") == 0)
{
Debug.Log("Hello world!");
}
}
I'm looking to reduce the amount of code I have to repeat for every property, it's already down quite a way, but I'm hoping to make it as simple as possible, but any other comments on the code are welcome too.