Getting a list of mecanim states in Animator

I was just getting started with mecanim today. I really like the way it’s set up, but a few questions arose:

  • how can I just trigger an animation (e.g. “Dying”) and fade it in immediately?

  • this unity doc page states that you should “Implement a small AI layer to control the Animato. You can make it provide simple callbacks for OnStateChange, OnTransitionBegin etc…”. I think events are very much needed for fine-grained animation control, but I have no idea how to implement that given my third question:

  • how to get a list of states/transitions (and their names/hashes) from an Animator?

  1. To trigger an animation immediately from anywhere, create a transition from Any State to your Dying state. Create a bool parameter called “Dying” and set the transition to trigger whenever the Dying parameter is true. Then in your control script call animator.SetBool(“Dying”, true). (Side note: Per Unity - Manual: Performance and optimization, using a hash is better than the string “Dying”).

  2. Mecanim doesn’t yet implement events. You can use curves. I was doing this for some animations and triggering an “event” whenever the curve crossed the zero-axis. But now I use G1NurX’s Event System for Mecanim (Event System for Mecanim [Unity5 supported] | Animation Tools | Unity Asset Store) – well worth the cost.

  3. To my knowledge, Animator doesn’t expose this information yet. You can get the current and next state, but not an enumeration of all states on any given layer. Hmm, it just occurred to me that G1NurX’s product does this somehow. When I get back to the office I’ll look into it.

This is a method that I made for my game, and it works:

void Start () {
	_animator = gameObject.GetComponent<Animator>();
}

public AnimationClip GetAnimationClip(string name) {
	if (!_animator) return null; // no animator

	foreach (AnimationClip clip in _animator.runtimeAnimatorController.animationClips) {
		if (clip.name == name) {
			return clip;
		}
	}
	return null; // no clip by that name
}

This is now available inside unity. It returns all the animation clips used by an actor. It works on both legacy prefabs and mecanim prefabs.

AnimationUtility.GetAnimationClips(GameObject)

Here’s the editor-time method I use to get an array of states from the Animator component:

private static AnimatorState[] GetStateNames(Animator animator) {
	AnimatorController controller = animator ? animator.runtimeAnimatorController as AnimatorController : null;
	return controller == null ? null : controller.layers.SelectMany(l => l.stateMachine.states).Select(s => s.state).ToArray();
}

Very good content here. My problem is I’m setting the layer weight and it’s playing at the wrong starting point of the animation. I want to be able to reset the state time so the animation plays from the beginning. Cross-fading just masks the issue.

I was able to at least determine the length of the state animation given the animation clip is found within Resources.

            if (Random.Range(0, 2) == 0)
            {
                SetLayerWeight("Attack", 1f);
                SetLayerWeight("Cast", 0f);
            }
            else
            {
                SetLayerWeight("Attack", 0f);
                SetLayerWeight("Cast", 1f);
            }
            SetLayerWeight("Idle", 0f);

            UnityEditorInternal.AnimatorController ac = m_animator.runtimeAnimatorController as UnityEditorInternal.AnimatorController;
            for (int count = 0; count < m_animator.layerCount; ++count)
            {
                if (m_animator.GetLayerWeight(count) == 1f)
                {
                    Debug.Log(m_animator.GetLayerName(count));
                    Debug.Log(ac.GetLayer(count).stateMachine.GetState(0).name);
                    m_animator.Play(ac.GetLayer(count).stateMachine.GetState(0).name);
                    AnimationClip clip = (AnimationClip)Resources.Load(ac.GetLayer(count).stateMachine.GetState(0).name, typeof(AnimationClip));
                    if (clip)
                    {
                        Debug.Log(clip.length);
                        m_timer2 = DateTime.Now + TimeSpan.FromSeconds(clip.length);
                        break;
                    }
                }
            }

So we used a similar solution, but we did it because we have our own LOD management instead of Unity’s LOD.

Ours is called TDLLOD. We found that in runtime (not in editor) that setting the state on
an Animator causes it to permantly lock up. So we first check that it is enabled and accessible.

This code works for us. Note: watch out for the difference between name and uniqueName!

	public bool IsValidAnimatorState( Animator a, string stateName, int layer )
	{
		bool ret = false;

		try
		{
			layer = CheckLayer(layer);
			if(layer == -1)
			{
				layer = 0;
			}

			UnityEditorInternal.AnimatorController ac = a.runtimeAnimatorController as UnityEditorInternal.AnimatorController;
			if( !ac )
			{
				return false;
			}

			//int layerCount = ac.layerCount;
			//Debug.Log(string.Format("Layer Count: {0}", layerCount));
			
			// Names of each layer:
	//		for (int ly = 0; ly < layerCount; ly++) 
	//		{
	//			Debug.Log(string.Format("Layer {0}: {1}", layer, ac.GetLayer(ly).name));
	//		}

			UnityEditorInternal.AnimatorControllerLayer ly = ac.GetLayer(layer);
			if( ly == null )
			{
				return false;
			}

			UnityEditorInternal.StateMachine sm = ly.stateMachine;
			if( !sm )
			{
				return false;
			}

			for(int i=0; i< sm.stateCount; i++)
			{
				// .name is like "Buck_death"  uniqueName is like "Base Layer.Buck_death".
				// Debug.Log(string.Format("State: {0}", sm.GetState(i).name));
				ret |= (sm.GetState(i).name == stateName);
			}

			if(ret == false)
			{
				//stateValidDebugFlag = true;
			}
		}
		catch( System.Exception exx )
		{
		}

		return ret;
	}

My answer is very simple and cool

int hash =  Animator.StringToHash("Dying");

GetComponent().Play(hash, 0, 1);

I am getting the name of all Animation States in my Animator component in my Reset Method. I do pass through mecanim states to achieve that. Take a look at my code it might help you list all mech anim states.[128495-nameallanimstates.txt|128495]

I made an update working with the current APIs. I hope someone finds this helpful!

using UnityEditor.Animations;

public void PrintStates()
        {
            // Get a reference to the Animator Controller:
            AnimatorController ac = _anim.runtimeAnimatorController as AnimatorController;

            // Number of layers:
            int layerCount = ac.layers.Length;
            Debug.Log(string.Format("Layer Count: {0}", layerCount));

            // Names of each layer:
            for (int layer = 0; layer < layerCount; layer++)
            {
                Debug.Log(string.Format("Layer {0}: {1}", layer, ac.layers[layer].name));
            }

            // States on layer 0:
            AnimatorStateMachine sm = ac.layers[0].stateMachine;
            ChildAnimatorState[] states = sm.states; // Also: sm.states
            foreach (ChildAnimatorState s in states)
            {
                Debug.Log(string.Format("State: {0}", s.state.name));
            }
        }