Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Call method when dropdown item is selected in Inspector

Discussion in 'Scripting' started by ben4d85, Mar 23, 2023.

  1. ben4d85

    ben4d85

    Joined:
    Dec 26, 2018
    Posts:
    47
    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:
    Code (CSharp):
    1. public class Animal : MonoBehaviour
    2. {
    3.     public enum AnimalType
    4.     {
    5.         Dog, Cat, Hamster, Bunny,
    6.     }
    7.  
    8.     [SerializeField] private AnimalType animalType;
    9.  
    10.     // TODO: Call this function every time I select a value
    11.     // from the enum dropdown in the Inspector.
    12.     private void OnAnimalSelected() { }
    13. }
     
  2. RadRedPanda

    RadRedPanda

    Joined:
    May 9, 2018
    Posts:
    1,593
  3. ben4d85

    ben4d85

    Joined:
    Dec 26, 2018
    Posts:
    47
    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.
     
    Last edited: Mar 23, 2023
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,893
    The answer is via a custom inspector.
     
    SisusCo likes this.
  5. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,114
    You can achieve this with a custom property attribute and property drawer.

    Something like this:
    Code (CSharp):
    1. [AttributeUsage(AttributeTargets.Field), Conditional("UNITY_EDITOR")]
    2. public sealed class OnValueChangedAttribute : PropertyAttribute
    3. {
    4.     public string MethodName { get; }
    5.  
    6.     public OnValueChangedAttribute(string methodName) => MethodName = methodName;
    7. }
    Code (CSharp):
    1. [CustomPropertyDrawer(typeof(OnValueChangedAttribute))]
    2. public class OnValueChangedAttributeDrawer : PropertyDrawer
    3. {
    4.     public override VisualElement CreatePropertyGUI(SerializedProperty property)
    5.     {
    6.         var result = new PropertyField(property);
    7.         result.RegisterValueChangeCallback(OnValueChanged);      
    8.         return result;
    9.  
    10.         static void OnValueChanged(SerializedPropertyChangeEvent @event)
    11.         {
    12.             const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
    13.             var serializedProperty = @event.changedProperty;
    14.             var targets = serializedProperty.serializedObject.targetObjects;
    15.             var type = targets.First().GetType();
    16.             var field = type.GetField(serializedProperty.name, flags);
    17.             var attribute = field.GetCustomAttributes(typeof(OnValueChangedAttribute), false).First() as OnValueChangedAttribute;
    18.             var method = type.GetMethod(attribute.MethodName, flags);
    19.             Undo.RecordObjects(targets, "On Value Changed");
    20.             foreach(var target in targets)
    21.             {
    22.                 var parameters = method.GetParameters();
    23.                 switch(parameters.Length)
    24.                 {
    25.                     case 0:
    26.                         method.Invoke(target, null);
    27.                         break;
    28.                     case 1 when parameters[0].ParameterType is Type parameterType:
    29.                         if(parameterType == typeof(SerializedProperty))
    30.                         {
    31.                             method.Invoke(target, new object[] { serializedProperty });
    32.                         }
    33.                         else
    34.                         {
    35.                             method.Invoke(target, new object[] { field.GetValue(target) });
    36.                         }
    37.                         break;
    38.                 }
    39.             }
    40.             serializedProperty.serializedObject.Update();
    41.         }
    42.     }
    43. }
    And usage:
    Code (CSharp):
    1. [OnValueChanged(nameof(OnValueChanged))]
    2. private float value;
    3.  
    4. private void OnValueChanged(float value)
    5. {
    6.     Debug.Log("Value changed to: " + value);
    7. }
     
    Last edited: Mar 26, 2023
    ben4d85 likes this.