Skip to content

Instantly share code, notes, and snippets.

@codeimpossible
Forked from LotteMakesStuff/AutohookAttribute.cs
Last active October 1, 2019 04:27
Show Gist options
  • Save codeimpossible/e7629352059441ffd7cd20e212e098b8 to your computer and use it in GitHub Desktop.
Save codeimpossible/e7629352059441ffd7cd20e212e098b8 to your computer and use it in GitHub Desktop.

Revisions

  1. codeimpossible revised this gist Oct 1, 2019. 3 changed files with 35 additions and 4 deletions.
    11 changes: 11 additions & 0 deletions AutohookAttribute.cs
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,17 @@
    // NOTE DONT put in an editor folder!
    using UnityEngine;

    public enum AutohookSearchArea {
    Self,
    Parent,
    Children
    }

    public class AutohookAttribute : PropertyAttribute
    {
    public AutohookSearchArea SearchArea { get; private set; }

    public AutohookAttribute(AutohookSearchArea searchArea = AutohookSearchArea.Self) {
    SearchArea = searchArea;
    }
    }
    20 changes: 19 additions & 1 deletion AutohookPropertyDrawer.cs
    Original file line number Diff line number Diff line change
    @@ -46,6 +46,7 @@ public override float GetPropertyHeight(SerializedProperty property, GUIContent
    private Component FindAutohookTarget(SerializedProperty property)
    {
    var root = property.serializedObject;
    var attribute = PropertyUtility.GetAttribute<AutohookAttribute>(property);

    if (root.targetObject is Component)
    {
    @@ -54,7 +55,12 @@ private Component FindAutohookTarget(SerializedProperty property)

    // ...then use GetComponent(type) to see if there is one on our object.
    var component = (Component)root.targetObject;
    return component.GetComponent(type);
    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
    {
    @@ -79,4 +85,16 @@ private static System.Type GetTypeFromProperty(SerializedProperty property)
    // ... using that we can return the raw .net type!
    return fieldInfo.FieldType;
    }

    /// <summary>
    /// Uses reflection to get the attribute from a serialized property
    /// </summary>
    /// <param name="property"></param>
    /// <returns></returns>
    public static T GetAttribute<T>(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;
    }
    }
    8 changes: 5 additions & 3 deletions readme.md
    Original file line number Diff line number Diff line change
    @@ -2,9 +2,12 @@ heres a tiny little example of how to use it
    ```C#
    public class AutohookTest : MonoBehaviour
    {
    [Autohook] // <-- yeah its that easy!
    [Autohook] // <-- will match Rigidbody on the current gameobject
    public Rigidbody rigidbody;

    [Autohook(AutohookSearchArea.Children)] // <-- will search children for first AudioSource
    public AudioSource audio;

    // Update is called once per frame
    void Update()
    {
    @@ -13,6 +16,5 @@ public class AutohookTest : MonoBehaviour
    }
    }
    ```
    You can watch a demo of this in action here https://youtu.be/faVt09NGzws

    [![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/A08215TT)
    The `AutoHook` attribute was originally coded by [LotteMakesStuff](https://gist.github.com/LotteMakesStuff). If you find it usefule you should [support them on ko-fi](https://ko-fi.com/A08215TT)!
  2. @LotteMakesStuff LotteMakesStuff revised this gist Apr 5, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion readme.md
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@ heres a tiny little example of how to use it
    ```C#
    public class AutohookTest : MonoBehaviour
    {
    [Autohook]
    [Autohook] // <-- yeah its that easy!
    public Rigidbody rigidbody;

    // Update is called once per frame
  3. @LotteMakesStuff LotteMakesStuff revised this gist Apr 5, 2019. 1 changed file with 18 additions and 0 deletions.
    18 changes: 18 additions & 0 deletions readme.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,18 @@
    heres a tiny little example of how to use it
    ```C#
    public class AutohookTest : MonoBehaviour
    {
    [Autohook]
    public Rigidbody rigidbody;

    // Update is called once per frame
    void Update()
    {
    // do something
    rigidbody.AddForce(Vector3.up);
    }
    }
    ```
    You can watch a demo of this in action here https://youtu.be/faVt09NGzws

    [![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/A08215TT)
  4. @LotteMakesStuff LotteMakesStuff revised this gist Apr 5, 2019. No changes.
  5. @LotteMakesStuff LotteMakesStuff revised this gist Apr 5, 2019. No changes.
  6. @LotteMakesStuff LotteMakesStuff created this gist Apr 5, 2019.
    6 changes: 6 additions & 0 deletions AutohookAttribute.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,6 @@
    // NOTE DONT put in an editor folder!
    using UnityEngine;

    public class AutohookAttribute : PropertyAttribute
    {
    }
    82 changes: 82 additions & 0 deletions AutohookPropertyDrawer.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,82 @@
    // 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);
    }

    /// <summary>
    /// 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
    /// </summary>
    /// <param name="property"></param>
    /// <returns></returns>
    private Component FindAutohookTarget(SerializedProperty property)
    {
    var root = property.serializedObject;

    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;
    return component.GetComponent(type);
    }
    else
    {
    Debug.Log("OH NO handle fails here better pls");
    }

    return null;
    }

    /// <summary>
    /// Uses reflection to get the type from a serialized property
    /// </summary>
    /// <param name="property"></param>
    /// <returns></returns>
    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;
    }
    }