Call method when dropdown item is selected in Inspector

I would like a method to be called every time I select a value from a dropdown in the Inspector. Is this possible?

Code sample:

public class Animal : MonoBehaviour
{
    public enum AnimalType
    {
        Dog, Cat, Hamster, Bunny,
    }

    [SerializeField] private AnimalType animalType;

    // TODO: Call this function every time I select a value
    // from the enum dropdown in the Inspector.
    private void OnAnimalSelected() { }
}

Think you might be looking for this.

2 Likes

Thanks, but that one gets called more often than I need it to be. It also gets called very early, seemingly before Awake, and because of that I cannot access components in it that I have set with GetComponent in Awake.

The answer is via a custom inspector.

1 Like

You can achieve this with a custom property attribute and property drawer.

Something like this:

[AttributeUsage(AttributeTargets.Field), Conditional("UNITY_EDITOR")]
public sealed class OnValueChangedAttribute : PropertyAttribute
{
    public string MethodName { get; }

    public OnValueChangedAttribute(string methodName) => MethodName = methodName;
}
[CustomPropertyDrawer(typeof(OnValueChangedAttribute))]
public class OnValueChangedAttributeDrawer : PropertyDrawer
{
    public override VisualElement CreatePropertyGUI(SerializedProperty property)
    {
        var result = new PropertyField(property);
        result.RegisterValueChangeCallback(OnValueChanged);      
        return result;

        static void OnValueChanged(SerializedPropertyChangeEvent @event)
        {
            const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
            var serializedProperty = @event.changedProperty;
            var targets = serializedProperty.serializedObject.targetObjects;
            var type = targets.First().GetType();
            var field = type.GetField(serializedProperty.name, flags);
            var attribute = field.GetCustomAttributes(typeof(OnValueChangedAttribute), false).First() as OnValueChangedAttribute;
            var method = type.GetMethod(attribute.MethodName, flags);
            Undo.RecordObjects(targets, "On Value Changed");
            foreach(var target in targets)
            {
                var parameters = method.GetParameters();
                switch(parameters.Length)
                {
                    case 0:
                        method.Invoke(target, null);
                        break;
                    case 1 when parameters[0].ParameterType is Type parameterType:
                        if(parameterType == typeof(SerializedProperty))
                        {
                            method.Invoke(target, new object[] { serializedProperty });
                        }
                        else
                        {
                            method.Invoke(target, new object[] { field.GetValue(target) });
                        }
                        break;
                }
            }
            serializedProperty.serializedObject.Update();
        }
    }
}

And usage:

[OnValueChanged(nameof(OnValueChanged))]
private float value;

private void OnValueChanged(float value)
{
    Debug.Log("Value changed to: " + value);
}
1 Like