unity3d Unity Animator.参数在自定义PropertyDrawer中返回空数组

p5cysglq  于 2022-11-25  发布在  其他
关注(0)|答案(2)|浏览(128)

我创建了一个自定义的PropertyDrawer,它为用我的属性修饰的某个字段显示同一脚本中引用的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重新编译时,我得到了正确的值,代码再次工作。
这是什么原因,我该如何解决它?

zmeyuzjn

zmeyuzjn1#

在您的AnimationPropertyDrawer文件中,您希望“重新绑定”所有属性,以便我们看到最新的值。这是一个编辑器PropertyDrawer,所以我们开始期待一些神奇的功能是有代价的。幸运的是,这不会影响构建。
试试看:

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

请注意额外的行animator.Rebind ( );

pgky5nke

pgky5nke2#

Milan的答案解决了这个问题,但是如果animator.parameters所连接的gameobject是非活动的,它仍然会返回一个空数组。
我已经找到了一个解决方案,似乎在this forum中一直有效。
基本上,我需要从AnimatorController而不是Animator组件获取参数。
这是我的更新代码:

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

            // THIS IS THE SOLUTION
            var parameters = ((AnimatorController)animator.runtimeAnimatorController).parameters;
            var options = parameters
                .Where(parameter => a.AllowedParameters.Contains(parameter.type))
                .Select(parameter => parameter.name)
                .ToArray();
            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.");
    }
}

相关问题