// NOTE put in a Editor folder
using UnityEditor;
using UnityEngine;
[CustomPropertyDrawer(typeof(AutohookAttribute))]
public class AutohookPropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
// First, lets attempt to find a valid component we could hook into this property
var component = FindAutohookTarget(property);
if (component != null)
{
// if we found something, AND the autohook is empty, lets slot it.
// the reason were straight up looking for a target component is so we
// can skip drawing the field if theres a valid autohook.
// this just looks a bit cleaner but isnt particularly safe. YMMV
if (property.objectReferenceValue == null)
property.objectReferenceValue = component;
return;
}
// havent found one? lets just draw the default property field, let the user manually
// hook something in.
EditorGUI.PropertyField(position, property, label);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
// if theres a valid autohook target we skip drawing, so height is zeroed
var component = FindAutohookTarget(property);
if (component != null)
return 0;
// otherwise, return its default height (which should be the standard 16px unity usually uses)
return base.GetPropertyHeight(property, label);
}
///
/// Takes a SerializedProperty and finds a local component that can be slotted into it.
/// Local in this context means its a component attached to the same GameObject.
/// This could easily be changed to use GetComponentInParent/GetComponentInChildren
///
///
///
private Component FindAutohookTarget(SerializedProperty property)
{
var root = property.serializedObject;
var attribute = PropertyUtility.GetAttribute(property);
if (root.targetObject is Component)
{
// first, lets find the type of component were trying to autohook...
var type = GetTypeFromProperty(property);
// ...then use GetComponent(type) to see if there is one on our object.
var component = (Component)root.targetObject;
switch (attribute.SearchArea)
{
case AutohookSearchArea.Self: return component.GetComponent(type);
case AutohookSearchArea.Parent: return component.GetComponentInParent(type);
case AutohookSearchArea.Children: return component.GetComponentInChildren(type);
}
}
else
{
Debug.Log("OH NO handle fails here better pls");
}
return null;
}
///
/// Uses reflection to get the type from a serialized property
///
///
///
private static System.Type GetTypeFromProperty(SerializedProperty property)
{
// first, lets get the Type of component this serialized property is part of...
var parentComponentType = property.serializedObject.targetObject.GetType();
// ... then, using reflection well get the raw field info of the property this
// SerializedProperty represents...
var fieldInfo = parentComponentType.GetField(property.propertyPath);
// ... using that we can return the raw .net type!
return fieldInfo.FieldType;
}
///
/// Uses reflection to get the attribute from a serialized property
///
///
///
public static T GetAttribute(SerializedProperty property) where T : Attribute
{
FieldInfo fieldInfo = ReflectionUtility.GetField(GetTargetObject(property), property.name);
T[] attributes = (T[])fieldInfo.GetCustomAttributes(typeof(T), true);
return attributes.Length > 0 ? attributes[0] : null;
}
}