0

I have not very much experience regarding editor scripts so there is probably something very obvious I'm missing. Hopefully someone can give me a hand with this.

I'm trying to write a very simple editor script for a MonoBehaviour which includes an animator. The idea is to show in the inspector tab a Popup window with all the triggers in the Animator so anyone can be selected and then saved in a string variable in the MonoBehaviour script. The final goal for this is to create a prefab with any given animator and this MonoBehaviour script that can be instantiated multiple times in any scene, then select specific triggers in the Popup window to make the animator go through different transitions.

My classes are like this:

[CustomEditor(typeof (AnimationScript))]
public class AnimationScript_Editor : Editor
{
    
    public override void OnInspectorGUI()
    {
        this.DrawDefaultInspector();
        AnimationScript targetObject = (AnimationScript) this.target;
        
        var indexProperty =  serializedObject.FindProperty("selectedIndex") ;
        var triggerNameProperty = serializedObject.FindProperty("m_selectedTrigger");
        
        Animator animator = targetObject.p_Animator;
        if (animator == null)
            return;

        
        List<string> triggerOnlyNames = animator.parameters.ToList().FindAll(x => x.type == AnimatorControllerParameterType.Trigger)
                                                            .Select(x => x.name).ToList();

        triggerNameProperty.stringValue = triggerOnlyNames[indexProperty.intValue];
        indexProperty.intValue = EditorGUILayout.Popup(indexProperty.intValue, triggerOnlyNames.ToArray());


        serializedObject.ApplyModifiedProperties();
     
    }
}
public class AnimationScript : MonoBehaviour
{
    //public MyEnum animationList;

    [ContextMenu(" test play animation")]
    private void Update()
    {
        Debug.Log("---> " + m_selectedTrigger);
    }

    public Animator p_Animator => m_Animator;
    [SerializeField] private Animator m_Animator;
     public string m_selectedTrigger = "none";
    public int selectedIndex = 0;
}

Is working as expected, the list of triggers is loaded as a list of strings in the Popup and I can select any of them. I can even run the editor after doing that and I see in the Debug.Log from Animation.Update the very same selected string.

However, something weird happens when I try to save the scene by going to File-->save after making a selection (which is the ultimate goal of this),I get an exception: 'ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.'

Now, this happens exactly in the line

triggerNameProperty.stringValue = triggerOnlyNames[indexProperty.intValue];

and when I go step by step I see that animator.parameters has a length of 0 so the exception itself makes perfect sense but I can't understand why that happens. I also noticed that, despite the exception, if I close the scene and then load it again the change was properly saved and tthere is no exceptions anymore.

Can anybody give me a glimpse of why is this hapening and, hopefully, how to make it work properly??

Thanks in advance

1 Answer 1

1

The parameters are not directly stored (serialized) into the Animator component itself but rather only in the according AnimatorController asset.

If you look into the Animator source code you will see that parameters is not a serialized field but a getter property:

    extern public AnimatorControllerParameter[] parameters
    {
        [FreeFunction(Name = "AnimatorBindings::GetParameters", HasExplicitThis = true)]
        get;
    }

So my guess would be that during the serialization/deserialization process the Animator.parameters for a split second actually returns an empty array in the moment the according AnimatorController is not yet deserialized again.

So I think actually you could just avoid drawing the according fields at all in that case and do e.g.

[CustomEditor(typeof (AnimationScript))]
public class AnimationScript_Editor : Editor
{
    private SerializedProperty indexProperty; 
    private SerializedProperty triggerNameProperty;
    private Animator animatorProperty;

    private void OnEnable()
    {
        // It's enough to doo these only once ;)
        animatorProperty = serializedObject.FindProperty("m_Animator"); 
        indexProperty =  serializedObject.FindProperty("selectedIndex") ;
        triggerNameProperty = serializedObject.FindProperty("m_selectedTrigger");
    }
 
    public override void OnInspectorGUI()
    {
        this.DrawDefaultInspector();               
        
        var animator = (Animator)animatorProperty.objectValue;
        if (!animator)
            return;
        
        var triggerOnlyNames = animator.parameters.Where(x => x.type == AnimatorControllerParameterType.Trigger).Select(x => x.name).ToList();

        if(triggerOnlyNames.Count == 0)
            return;

        serializedObject.Update();

        triggerNameProperty.stringValue = triggerOnlyNames[indexProperty.intValue];
        indexProperty.intValue = EditorGUILayout.Popup(indexProperty.intValue, triggerOnlyNames.ToArray());


        serializedObject.ApplyModifiedProperties();        
    }
}

As alternative I think you could also directly go through the according AnimatorController like e.g.

...

var animator = targetObject.p_Animator;
if (!animator)
    return;

var controller = (AnimatorController) animator.runtimeAnimatorController;
        
var triggerOnlyNames = controller.parameters.Where(x => x.type == AnimatorControllerParameterType.Trigger).Select(x => x.name).ToList();

...
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the reply, was very helpful. I still don't know for sure what's happening. I tried the first thing you proposed but it didn't worked; I'm stil going to try a few more things based on that. The alternative proposal, however, it did work!! Again, I don't fully understand why but if take the reference to the AnimatorController rather than the animator then it is properly maintained when I save the scene (so the parameters are always there). If i find anything new regarding what happens to the Animator I will share it here but with this the urgent issue is solved. Thanks again!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.