Skip to content

Instantly share code, notes, and snippets.

@phobos2077
Last active September 7, 2018 16:55
Show Gist options
  • Save phobos2077/0b9fb1e5a74f872850e2d67dd061109e to your computer and use it in GitHub Desktop.
Save phobos2077/0b9fb1e5a74f872850e2d67dd061109e to your computer and use it in GitHub Desktop.
Interface-based object references in Unity
public class Example : MonoBehaviour
{
[SerializeField]
private MyInterfaceReference reference;
private void Awake()
{
reference.Value.Quack();
}
}
public interface IMyInterface
{
void Quack();
}
[Serializable]
public class MyInterfaceReference : InterfaceReference<IMyInterface>
{
public override bool AllowSceneObjects => true;
}
using System;
using UnityEngine;
/// <summary>
/// Base class required for the property drawer.
/// </summary>
[Serializable]
public class InterfaceReferenceBase
{
public virtual bool AllowSceneObjects => true;
[SerializeField]
public UnityEngine.Object RawValue;
}
[Serializable]
public class InterfaceReference<T> : InterfaceReferenceBase where T: class
{
public T Value
{
get { return RawValue as T; }
set { RawValue = value as UnityEngine.Object; }
}
public static implicit operator T(InterfaceReference<T> reference)
{
return reference.Value;
}
public static implicit operator InterfaceReference<T>(T value)
{
return new InterfaceReference<T>
{
Value = value
};
}
}
using UnityEngine;
using UnityEditor;
[CustomPropertyDrawer(typeof(InterfaceReferenceBase), true)]
class InterfaceReferenceAttributePropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var refType = fieldInfo.FieldType;
if (refType.BaseType != null && refType.BaseType.IsGenericType)
{
var interfaceType = refType.BaseType.GenericTypeArguments[0];
Debug.Assert(interfaceType != null);
label = EditorGUI.BeginProperty(position, label, property);
SerializedProperty valueProperty = null;
foreach (SerializedProperty prop in property)
{
if (prop.name == "RawValue")
{
valueProperty = prop;
break;
}
}
Debug.Assert(valueProperty != null);
// Hack to allow dragging GameObjects containing Behaviors that implement target interface
var indentedRect = EditorGUI.IndentedRect(position);
var e = Event.current;
if (e.type == EventType.DragUpdated && indentedRect.Contains(e.mousePosition) && GUI.enabled)
{
var dragged = DragAndDrop.objectReferences;
if (dragged.Length > 0 && dragged[0] is GameObject)
{
var suitableComponent = (dragged[0] as GameObject).GetComponent(interfaceType);
if (suitableComponent)
{
// Force drag restart
e.type = EventType.MouseDrag;
DragAndDrop.PrepareStartDrag();
dragged[0] = suitableComponent;
DragAndDrop.objectReferences = dragged;
DragAndDrop.StartDrag("Drag");
}
}
}
valueProperty.objectReferenceValue = EditorGUI.ObjectField(
position,
label,
valueProperty.objectReferenceValue,
interfaceType,
true);
EditorGUI.EndProperty();
}
else
{
EditorGUI.PropertyField(position, property, label, true);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment