- Home /
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?
Answer by TonyLi · May 07, 2013 at 03:26 PM
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 http://docs.unity3d.com/Documentation/Manual/MecanimPeformanceandOptimization.html, using a hash is better than the string "Dying").
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 (http://u3d.as/content/g1nur-x/event-system-for-mecanim/3QP) -- well worth the cost.
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.
Hey, there is a way! It doesn't appear to be documented, so there's no guarantee that a later version of Unity won't change things. But here's how to do it:
// Get a reference to the Animator Controller:
UnityEditorInternal.AnimatorController ac = GetComponent<Animator>().runtimeAnimatorController as UnityEditorInternal.AnimatorController;
// Number of layers:
int layerCount = ac.GetLayerCount();
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.GetLayerName(layer)));
}
// States on layer 0:
UnityEditorInternal.State$$anonymous$$achine sm = ac.GetLayerState$$anonymous$$achine(0);
List<UnityEditorInternal.State> states = sm.statesRecursive; // Also: sm.states
foreach (UnityEditorInternal.State s in states) {
Debug.Log(string.Format("State: {0}", s.GetUniqueName()));
}
In addition to State.GetUniqueName(), there's State.GetUniqueNameHash(), and for transitions:
transitionCount
transitions
GetTransitionsFromState()
etc.
Wow, thanks for the elaborate answer and for actually looking into that again.
Awesome stuff! This really should not be internal stuff.
The API has indeed changed, this is working for 4.3:
// States on layer 0:
UnityEditorInternal.State$$anonymous$$achine sm = ac.GetLayer(0).state$$anonymous$$achine;
for (int i = 0; i < sm.stateCount; i++) {
UnityEditorInternal.State state = sm.GetState(i);
Debug.Log(string.Format("State: {0}", state.uniqueName));
}
I created an open source asset called Animator Access Generator, that generates a C# class as $$anonymous$$onoBehaviour which contains the hash IDs of all states and parameters. So this information is available at runtime too.
See the forum posting. I generate a couple of convenience methods for checking states and accessing parameters and there is a StateIdToName
method to get the clear text name for a hash ID.
Thanks to all the valuable comments here that inspired me :)
Note that as of Unity 5 this now sits in UnityEditor.Animations, and is documented (http://docs.unity3d.com/ScriptReference/Animations.AnimatorController.html).
Answer by MilanGM · Jul 15, 2015 at 10:45 AM
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
}
If you want could turn that method static so don't need to copy it around in classes:
internal static AnimationClip GetAnimationClipFromAnimatorByName(Animator animator, string name)
{
//can't get data if no animator
if (animator == null)
return null;
//favor for above foreach due to performance issues
for (int i = 0; i < animator.runtimeAnimatorController.animationClips.Length; i++)
{
if (animator.runtimeAnimatorController.animationClips [i].name == name)
return animator.runtimeAnimatorController.animationClips [i];
}
Debug.LogError ("Animation clip: " +name + " not found");
return null;
}
also good to note: name of animation is not the name inside the Animator but of the Animation file itself
just a note. This method doesn't work when you put a bunch of animations with the same name (i.e. mixamo.com) because the clip name is "mixamo.com" but the states are renamed to "mixamo.com 0, 1, ..."
Answer by akelsey · Apr 28, 2015 at 03:15 AM
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)
Answer by bjennings76 · Dec 01, 2016 at 02:22 AM
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();
}
Answer by frattapa · Jul 16, 2020 at 02:19 PM
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));
}
}
Your answer
Follow this Question
Related Questions
Mecanim add weight to certain animations 0 Answers
3rd person gta like weapon problem?? 0 Answers
Mecanim Boolean 1 Answer
Swap Mecanim Layers in Unity 4.2 0 Answers