Skip to content

Instantly share code, notes, and snippets.

@LotteMakesStuff
Last active July 1, 2024 09:54
Show Gist options
  • Save LotteMakesStuff/d6a9a4944fc667e557083108606b7d22 to your computer and use it in GitHub Desktop.
Save LotteMakesStuff/d6a9a4944fc667e557083108606b7d22 to your computer and use it in GitHub Desktop.
[Autohook] property drawer for unity - Add this [Autohook] attribute to a property to and the inspector will automagically hook up a valid reference for you if it can find a component attached to the same game object that matches the field you put it on. You can watch a demo of this in action here https://youtu.be/faVt09NGzws <3

heres a tiny little example of how to use it

public class AutohookTest : MonoBehaviour
{
    [Autohook] // <-- yeah its that easy!
    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

// NOTE DONT put in an editor folder!
using UnityEngine;
public class AutohookAttribute : PropertyAttribute
{
}
// 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;
}
}
@soraphis
Copy link

soraphis commented Apr 7, 2019

It would be usefull for Autohook to derive from SerializeField, since it does not work if the field is not serializable.

also adding: [AttributeUsage(AttributeTargets.Field)] to AutohookAttribute would be helpful

@Deadcows
Copy link

Deadcows commented Apr 8, 2019

You inspired me to write my own inplementation: AutoPropertyAttribute

It works automatically on Scene Save (you don't need to "look" at every object in inspector to check components) and also works with arrays (to grab child components of required type)

Source if you interested :) Thanks for this idea!

@ivo-alves
Copy link

It would be usefull for Autohook to derive from SerializeField, since it does not work if the field is not serializable.

also adding: [AttributeUsage(AttributeTargets.Field)] to AutohookAttribute would be helpful

You can't derive from SerializeField since it's sealed.

What you can do is look for private fields in the GetField call:

var fieldInfo = parentComponentType.GetField(property.propertyPath, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

So you can use it like this:

[Autohook, SerializeField] GraphicRaycaster raycaster;

Thanks for sharing

@gerwalk
Copy link

gerwalk commented Mar 11, 2020

Simple and clever, and frankly a bit of a game changer.. thanks for sharing this!

@mrwellmann
Copy link

I've seen something a bit more sophisticated like this here https://github.com/Nrjwolf/unity-auto-attach-component-attributes.

@bo11ox
Copy link

bo11ox commented Oct 11, 2021

Simple and clever, and frankly a bit of a game changer.. thanks for sharing this!

truly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment