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重新编译代码后,才能获取到正确的参数值。请问该问题的原因是什么?如何修复?
原因分析
- 编辑模式数据不同步:Unity编辑器中修改AnimatorController参数后,不会自动将修改同步到场景中Animator组件的运行时对象,直接访问
animator.parameters拿到的是旧缓存数据。 - 未监听资源变化:当前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(); } } }
关键改动说明
- 从资源层读取参数:直接获取
AnimatorController资源对象读取参数,确保拿到的是最新编辑后的内容,避免运行时缓存问题。 - 使用SerializedProperty访问字段:替代反射逻辑,更贴合Unity编辑器的序列化工作流,减少数据不同步风险。
- 添加资源监听:通过
AssetDatabase.imported事件监听AnimatorController资源的修改,自动刷新Inspector窗口,无需手动编译。 - 完善异常处理:增加控制器为空、无匹配参数的场景提示,提升编辑器使用体验。
内容的提问来源于stack exchange,提问作者SagiZiv
相关产品推荐
相关产品推荐

