You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity自定义PropertyDrawer中Animator.parameters返回空数组问题排查

问题:Animator参数修改后PropertyDrawer无法实时获取更新的参数列表

我开发了一个自定义PropertyDrawer,可为带有自定义特性的字段展示同一脚本内引用的Animator对象的参数,以此确保字段值为该Animator中的合法参数。

相关代码如下:

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);
    }
}

我的PropertyDrawer实现如下:

[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.");
    }
}

目前代码可正常运行,但修改Animator对象的参数后,animator.parameters会返回空数组;只有强制Unity重新编译代码后,才能获取到正确的参数值。请问该问题的原因是什么?如何修复?


原因分析

  1. 编辑模式数据不同步:Unity编辑器中修改AnimatorController参数后,不会自动将修改同步到场景中Animator组件的运行时对象,直接访问animator.parameters拿到的是旧缓存数据。
  2. 未监听资源变化:当前PropertyDrawer没有监听AnimatorController的修改事件,无法主动刷新UI获取最新参数列表。

修复方案

改为从AnimatorController序列化资源读取参数,同时监听资源变化触发UI刷新,修改后的AnimationParamDrawer代码如下:

using System.Linq;
using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(AnimationParamAttribute))]
public class AnimationParamDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        AnimationParamAttribute attr = (AnimationParamAttribute)attribute;
        if (property.propertyType != SerializedPropertyType.String)
        {
            EditorGUI.LabelField(position, label.text, "Use with string fields.");
            return;
        }

        // 通过SerializedProperty获取Animator引用,符合Unity序列化逻辑
        SerializedProperty animatorProp = property.serializedObject.FindProperty(attr.AnimatorName);
        if (animatorProp == null || animatorProp.objectReferenceValue == null)
        {
            EditorGUI.LabelField(position, label.text, "Select an animator.");
            return;
        }

        Animator animator = animatorProp.objectReferenceValue as Animator;
        AnimatorController controller = animator.runtimeAnimatorController as AnimatorController;
        if (controller == null)
        {
            EditorGUI.LabelField(position, label.text, "Animator has no valid controller.");
            return;
        }

        // 从AnimatorController资源读取最新参数
        var options = controller.parameters
            .Where(p => attr.AllowedParameters.Contains(p.type))
            .Select(p => p.name)
            .ToArray();

        if (options.Length == 0)
        {
            EditorGUI.LabelField(position, label.text, "No matching parameters found.");
            return;
        }

        int selection = Array.IndexOf(options, property.stringValue);
        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())
        {
            property.stringValue = options[selection];
            property.serializedObject.ApplyModifiedProperties();
        }
        EditorGUI.EndProperty();
    }

    // 监听AnimatorController资源变化,自动刷新Inspector
    [InitializeOnLoadMethod]
    private static void Initialize()
    {
        AssetDatabase.imported += OnAssetImported;
    }

    private static void OnAssetImported(string[] importedAssets)
    {
        if (importedAssets.Any(path => path.EndsWith(".controller")))
        {
            EditorApplication.RepaintInspector();
        }
    }
}

关键改动说明

  1. 从资源层读取参数:直接获取AnimatorController资源对象读取参数,确保拿到的是最新编辑后的内容,避免运行时缓存问题。
  2. 使用SerializedProperty访问字段:替代反射逻辑,更贴合Unity编辑器的序列化工作流,减少数据不同步风险。
  3. 添加资源监听:通过AssetDatabase.imported事件监听AnimatorController资源的修改,自动刷新Inspector窗口,无需手动编译。
  4. 完善异常处理:增加控制器为空、无匹配参数的场景提示,提升编辑器使用体验。

内容的提问来源于stack exchange,提问作者SagiZiv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 18:45:47