Home > Mobile >  Unity Animator.parameters returns empty array in custom PropertyDrawer
Unity Animator.parameters returns empty array in custom PropertyDrawer

Time:11-17

I created a custom PropertyDrawer that for a certain field decorated with my attribute, shows the parameters from the animator object referenced in the same script.

That way, I can make sure the field's value is a valid parameter in the referenced animator.

This is my code:


public class AnimationParamAttribute : PropertyAttribute
{
    public string AnimatorName { get; }
    public AnimatorControllerParameterType[] AllowedParameters { get; }
    public AnimationParamAttribute(string animatorName, params AnimatorControllerParameterType[] allowedParameters)
    {
        AnimatorName = animatorName;
        this.AllowedParameters = allowedParameters;
    }
}


public class AnimationController : MonoBehaviour
{
    [SerializeField] private Animator animator;
    [SerializeField, AnimationParam(nameof(animator), AnimatorControllerParameterType.Trigger)]
    private string trigger;

    [ContextMenu("Print value")]
    private void PrintValue()
    {
        Debug.Log(trigger);
    }

    public void StartAnimation()
    {
        animator.SetTrigger(trigger);
    }
}

And my PropertyDrawer looks like this:

[CustomPropertyDrawer(typeof(AnimationParamAttribute))]
public class AnimationParamDrawer : PropertyDrawer
{
    // Draw the property inside the given rect
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        AnimationParamAttribute a = (AnimationParamAttribute)attribute;
        if (property.propertyType == SerializedPropertyType.String)
        {
            var targetObject = property.serializedObject.targetObject;
            var field = targetObject.GetType().GetField(a.AnimatorName,
                BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
            Animator animator = field?.GetValue(targetObject) as Animator;
            if (animator == null)
            {
                EditorGUI.LabelField(position, label.text, "Select an animator.");
                return;
            }
            var options = animator.parameters
                .Where(parameter => a.AllowedParameters.Contains(parameter.type))
                .Select(parameter => parameter.name)
                .ToArray();
            var s = string.Join(", ", options);
            Debug.Log($"Options: {s}");
            int selection = Array.IndexOf(options, property.stringValue);
            Debug.Log($"{property.stringValue} is option {selection}");
            if (selection < 0) selection = 0;
            position = EditorGUI.PrefixLabel(position, label);
            EditorGUI.BeginProperty(position, label, property);
            EditorGUI.BeginChangeCheck();
            selection = EditorGUI.Popup(position, selection, options);
            if (EditorGUI.EndChangeCheck())
            {
                Debug.Log($"New selection: {selection}");
                property.stringValue = options[selection];
            }
            EditorGUI.EndProperty();
        }
        else
            EditorGUI.LabelField(position, label.text, "Use with string fields.");
    }
}

The code works fine, but for some reason when I change the parameters in the animator object, animator.parameters returns an empty array.

When I modify the code to force Unity to recompile, I get the correct values and the code works again.

What is the reason for this, and how can I fix it?

CodePudding user response:

In your AnimationPropertyDrawer file, you want to "rebind" all of the properties for us to see the most up-to-date values. This is an Editor PropertyDrawer, so we come to expect some of the magic comes at a price. Fortunately, this won't carry across to the build.

Try this:

Animator animator = field?.GetValue(targetObject) as Animator;
if ( animator == null )
{
    EditorGUI.LabelField ( position, label.text, "Select an animator." );
    return;
}
animator.Rebind ( );
var options = animator.parameters
    .Where(parameter => a.AllowedParameters.Contains(parameter.type))
    .Select(parameter => parameter.name)
    .ToArray();

Take note of the extra line animator.Rebind ( );.

enter image description here

  • Related