0
\$\begingroup\$

So I have an animator on my GameObject. I need to change the animation clip/motion of different states in my animator to the one I assign in my EditorGUILayout.ObjectField. Problem is, I have no idea how to access those clips. This is purely Editor Scripting.

\$\endgroup\$
1
  • \$\begingroup\$ You want to change it or replace it? You can create different states machine as per your requirements and use it through code. \$\endgroup\$
    – Saad Anees
    Jul 14, 2019 at 10:37

2 Answers 2

2
\$\begingroup\$

This is purely Editor Scripting.

Yes, you can do it. But take note that manipulating Animator's state machine and clip inside ONLY possible in Editor. (For automation editing/scripting, etc) It is very complicate process but I will try to cover as much as I can.

For side note, AnimationClipOverrides in Dmitri's answer is for game runtime. It 'overrides' the original animation clip to another set of animation clips.


Obtain reference to Editor's Animator

First in editor script or EditorGUI, obtain reference to UnityEditor.Animations.AnimatorController you are going to wotk with by any method, for example via field in custom EditorWindow:

using UnityEditor;
using UnityEditor.Animations;

AnimatorController m_CurrentAnimator;
m_CurrentAnimator = (AnimatorController)EditorGUILayout.ObjectField( "Animator", m_CurrentAnimator, typeof( AnimatorController ), false );

Access layer and get all states inside

First is layer that you have to peel off, for simplicity, I will operate on layer 0. Then there will be AnimatorStateMachine inside it, I will use List to collect all states recursively. (also travel through nesting sub-statemachines)

AnimatorControllerLayer[] layers = m_CurrentAnimator.layers;
AnimatorControllerLayer workingLayer = layers[0];

List<AnimatorState> stateList = _ExpandStatesInLayer( sourceStateInfoLayer.stateMachine );

where

List<AnimatorState> _ExpandStatesInLayer( AnimatorStateMachine sm, List<AnimatorState> collector = null )
{
    if( collector == null )
        collector = new List<AnimatorState>( );

    foreach( var state in sm.states )
    {
        collector.Add( state.state );

        foreach( var subSm in sm.stateMachines ) // Jump into nested state machine
            _ExpandStatesInLayer( subSm.stateMachine, collector );
    }
    return collector;
}

Peek into state's content (AnimationClip/BlendTree)

After long fight, now we can see contents in each state. The code is self explained. It is up to you how to modify these state.

foreach( var state in stateList )
{
    Motion m = state.motion;
    if( m is AnimationClip ) // It is single animation clip
        Debug.Log( "clip " + m.name );
    if( m is BlendTree ) // It is blendtree with multiple clips!
        Debug.Log( "tree " + m.name );

    state.motion = null; // TODO: Set something else to it.
}

  • Dont forget a call to UnityEditor.EditorUtility.SetDirty if you actually modify it. Or better alternative UnityEditor.Undo.RecordObject. (It also set dirty state)
  • If layer is SYNC layer (copy states from source layer), the workflow will be a bit different when getting state list and getting motion attached to it. It is not covered in this answer. You can lookup potential API from related class documents.
\$\endgroup\$
0
\$\begingroup\$

You cannot access the clip in the animator directly. Here is an example of how I used clip replacement. For more information, look into Unity's documents. https://docs.unity3d.com/ScriptReference/AnimatorOverrideController.ApplyOverrides.html

using UnityEngine;
using System.Collections.Generic;

public class AnimationClipOverrides : List<KeyValuePair<AnimationClip, AnimationClip>>
{
    public AnimationClipOverrides(int capacity) : base(capacity) { }

    public AnimationClip this[string name]
    {
        get { return this.Find(x => x.Key.name.Equals(name)).Value; }
        set
        {
            int index = this.FindIndex(x => x.Key.name.Equals(name));
            if (index != -1)
                this[index] = new KeyValuePair<AnimationClip, AnimationClip>(this[index].Key, value);
        }
    }
}

public class PlayerScript : MonoBehaviour
{
    [SerializeField]
    private AnimationClip[] m_Animations;

    private Animator anim;
    private AnimatorOverrideController animatorOverrideController;
    private AnimationClipOverrides clipOverrides;

    void Start()
    {
        anim = GetComponent<Animator>();
        animatorOverrideController = new AnimatorOverrideController(anim.runtimeAnimatorController);
        anim.runtimeAnimatorController = animatorOverrideController;

        clipOverrides = new AnimationClipOverrides(animatorOverrideController.overridesCount);
        animatorOverrideController.GetOverrides(clipOverrides);
    }

    public void Play(int numberClip)
    {
        clipOverrides["myAnimation"/*name *.anim file in State inspector*/] = m_Animations[numberClip];
        animatorOverrideController.ApplyOverrides(clipOverrides);
        anim.Play("start");

    }
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .