Skip to content

Instantly share code, notes, and snippets.

@James-Frowen
Last active July 20, 2023 15:07
Show Gist options
  • Save James-Frowen/bf35f063188974097a38cefa3a01594c to your computer and use it in GitHub Desktop.
Save James-Frowen/bf35f063188974097a38cefa3a01594c to your computer and use it in GitHub Desktop.
Unity3d, How to show an interface field in Inspector
namespace JamesFrowen
{
public interface ISomeInterface
{
void SomeMethod();
}
[System.Serializable]
public class SomeInterfaceWrapper : InterfaceWrapper<ISomeInterface>
{
// Unity cant serialize generics so a derived class must be used
}
}
using UnityEngine;
namespace JamesFrowen
{
public class UseExample : MonoBehaviour
{
// This field will be shown in insecptor.
// It can be set to any Unity Object (MonoBehaviour or ScriptableObject) that implements ISomeInterface
public SomeInterfaceWrapper someInterface;
public void Update()
{
this.someInterface.Value.SomeMethod();
}
}
}
using UnityEngine;
namespace JamesFrowen
{
[System.Serializable]
public abstract class InterfaceWrapper
{
[SerializeField] protected Object field;
public abstract System.Type InterfaceType();
// Used by Drawer
public UnityEngine.Object InternalField
{
get
{
return this.field;
}
set
{
this.field = value;
}
}
}
[System.Serializable]
public class InterfaceWrapper<T> : InterfaceWrapper where T : class
{
public T Value
{
get
{
return this.field as T;
}
set
{
this.field = value as UnityEngine.Object;
}
}
public override System.Type InterfaceType()
{
return typeof(T);
}
}
}
using UnityEngine;
using UnityEditor;
using System;
using System.Reflection;
namespace JamesFrowen.EditorScripts.CustomDrawers
{
[CustomPropertyDrawer(typeof(InterfaceWrapper), true)]
public class InterfaceWrapperDrawer : PropertyDrawer
{
private const BindingFlags BINDING_ATTR = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var wrapper = this.getWrapper(property);
if (wrapper == null)
{
EditorGUI.LabelField(position, new GUIContent(label.text + " Error Getting InterfaceWrapper"));
}
else
{
EditorGUI.BeginProperty(position, label, property);
EditorGUI.BeginChangeCheck();
var newValue = EditorGUI.ObjectField(position, label, wrapper.InternalField, wrapper.InterfaceType(), false);
var changed = EditorGUI.EndChangeCheck();
if (changed)
{
var field = property.FindPropertyRelative("field");
field.objectReferenceValue = newValue;
}
EditorGUI.EndProperty();
}
}
private InterfaceWrapper getWrapper(SerializedProperty property)
{
// This does not yet work with arrays or lists
var obj = property.serializedObject.targetObject;
var targetType = obj.GetType();
var fields = property.propertyPath.Split('.');
Type nextType = targetType;
object nextObject = obj;
for (int i = 0; i < fields.Length; i++)
{
var fieldInfo = nextType.GetField(fields[i], BINDING_ATTR);
if (fieldInfo == null)
{
return null;
}
nextObject = fieldInfo.GetValue(nextObject);
if (nextObject == null)
{
return null;
}
nextType = nextObject.GetType();
}
return nextObject as InterfaceWrapper;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment