When you command an Animator Controller (such as changing its state or setting a parameter), it will not actually respond until the next time it gets updated and even then it might still not do what you want.
For example, if you call Play
then check which state is currently playing it will still return the previous state until the next frame and if you call Play
again during the same frame it will ignore each call after the first instead of ending up in the last state you told it to play:
// Currently Playing Idle.
animator.Play("Jump");
AnimatorStateInfo state = animator.GetCurrentAnimatorStateInfo(0);
Debug.Log(state.IsName("Jump"));
This has several issues:
- It logs false because it's still Idle. It will only be playing the
Jump
animation next frame. - That means in order to wait for the animation to finish, we first have to wait a frame before we can find out its length.
- It assumes the
Jump
state is on layer 0 (it usually will be, but assumptions like that will cause bugs in more complex systems). - It is not possible to do something like
Debug.Log(state.name)
to find out what the state actually is. - That's a lot of text just to check what is playing.
// Assuming this happens in the same frame as the above.
animator.Play("Attack");
state = animator.GetCurrentAnimatorStateInfo(0);
Debug.Log(state.IsName("Attack"));
This has several more issues:
- It still logs false because it is still
Idle
. - But this time it will not be playing the
Attack
animation next frame, nor the frame after that. AllPlay
calls after the first are totally ignored, though it gives no error or other indication that this is happening. - So in order to function reliably, the script needs to wait a frame, verify that the
Attack
animation is actually playing (and if not, play it and wait another frame and check again), then it can finally check how long that animation will actually take to play.
On the other hand, Animancer responds to commands immediately, can properly handle multiple commands in the same frame, and allows you to easily check any details you want:
AnimationClip jump, attack;
// Currently Playing Idle.
AnimancerState jumpState = animancer.Play(jump);
Debug.Log(animancer.States.Current == jumpState);// True.
Debug.Log(animancer.IsPlaying(jump));// True.
Debug.Log(jumpState.IsPlaying);// True.
Debug.Log(jumpState.Clip.name);// The name of the AnimationClip.
Debug.Log(jumpState.Length);// The length of the AnimationClip.
AnimancerState attackState = animancer.Play(attack);
Debug.Log(animancer.IsPlaying(jump));// False (not jumping anymore).
Debug.Log(jumpState.IsPlaying);// False (not jumping anymore).
Debug.Log(animancer.IsPlaying(attack));// True.
Debug.Log(attackState.IsPlaying);// True.
Note that you are also less likely to even need to know the animation length because Animancer has several easy ways of Waiting for Animations to End.
Example
This video shows the character from Unity's 3D Game Kit appearing to float up into the air instead of playing the jump animation. The script checks if it is grounded to make sure it can jump, then tells the Animator Controller that it's not Grounded
anymore (you can see the parameter change) and starts moving them into the air. But the Animator Controller has its own separate set of rules to determine when to go to the jumping animation, which prevents it from doing what the script already made sure the character should be able to do.
If Unity themselves can't even use their own system reliably, how can anyone else be expected to?