// 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; } }