How can I check if an animation is playing or has finished? (Using Animator/C#)

I’m using the Animator component to animate my character in my game and C-Sharp to code it. I want the game to know if an animation is playing or has finished, because they are a lot of things I intend to code into the game that would require knowing if an animation is playing of finished.

I’ve looked it up, and the only solutions I’ve found were in Javascript and for the Animation component.

If it helps, I’ll put my situation in, my character pulls out a gun, I want the game to know that while it does that, the player cannot shoot, reload or change weapons until that animation is finished, how would I do that?

I’m talking about the Animator component, not Animation.

if (Input.GetKey(KeyCode.Mouse0)) { m_animator.SetBool("attack", true); ////si il etait dejas entrain de la jouer faite le sortir de la boucle avant de relancer pour etre en mesure de savoir quand esque le joueur relance sa boucle if (m_animator.GetCurrentAnimatorClipInfo(0)[0].clip.name == "MainAttack") { m_animator.SetBool("attack", false); } else { /// debue d'une nouvelle boucle } } else { m_animator.SetBool("attack", false); }

14 Answers

14

If you use the Animator and Mecanim, then I think you should do something like that :

if( this.animator.GetCurrentAnimatorStateInfo(0).IsName("YourAnimationName") )
{
   // Avoid any reload.
}

Be aware that your animation name should be similar to "Layer_Name"."State_Name" but if you use a "sub-state machine" in Mecanim then the animation name must be "Sub-StateMachine_Name"."State_Name".

I hope it may help you!

Cheers,
Flo

YES! It works! Thank you so much!

Is there one for when the animation has finished? Or even because I've now come across a situation where I would need to check that. Also, What does the number of the "GetCurrentAnimationStateInfo" represent?

The number is the layer

You could use AnimationState.normlizedTime to figure out the play count (decimal part) and the current progress (floating part). 2.25f would tell you that the animation has played two times and the current state is 25% (225%). Using modulo with 1.0f gives you always the progress. [AnimationState.normalizedTime][1] [1]: http://docs.unity3d.com/ScriptReference/AnimationState-normalizedTime.html

From what I understand, you would need to check this condition every frame; is there way that is more optimized, like an event that fires up when the animation finishes?

I check it with the time… after I switch to another animation (like with a trigger) I have following condition:

if( Animator.GetCurrentAnimatorStateInfo(0).normalizedTime>1 && !Animator.IsInTransition(0) )

Works like a charm for me!

The check for Animator is in transition check is mandatory cause otherwise there will be for 1 or 2 frames the time of the last state. Unity resets the time after the transition is completed.

punctuation man, use it!

Is there a way to specify what animation it's checking?

To know if Animator is playing any animation:

bool AnimatorIsPlaying(){
  return animator.GetCurrentAnimatorStateInfo(0).length >
         animator.GetCurrentAnimatorStateInfo(0).normalizedTime;
}

To know if is playing a specific animation:

bool AnimatorIsPlaying(string stateName){
  return AnimatorIsPlaying() && animator.GetCurrentAnimatorStateInfo(0).IsName(stateName);
}

Thanks this really helps me!!

This was what I needed, too. But Just a heads up here- comparing length with normalizedTime isn't what it seems. Length is the total seconds in the animation and normalizedTime is a mix of loop number and percentage completion- see https://docs.unity3d.com/ScriptReference/AnimationState-normalizedTime.html This will consider the animation no longer playing after it has completed one full animation: bool AnimatorIsPlaying(){ return animator.GetCurrentAnimatorStateInfo(0).normalizedTime < 1; } Took me a bit to figure why mine were lasting so long with edu4hd0's code.

The method shown to know if Animator is playing any animation does not work, it actually makes no sense (tested).

I cannot add some code in the comment space, so I have answered there… sorry…

There is no StateEnded or TransitionEnded event for now on the Mecanim system…
So the only way to do that is to pull the CurrentAnimatorStateInfo and check if you enter the state/leave it.

if(this.animator.GetCurrentAnimatorStateInfo(0).IsName("YourAnimationName"))
{
    // Avoid any reload.
    this.InMyState = true;
}
else if (this.InMyState)
{
     this.InMyState = false;
     // You have just leaved your state!
}

The number is the layer index. Basically, if you have not created a new layer in your Mecanim window, you only have one layer at index 0.

Alright, thanks anyway!

Hello, I had this same sort of problem recently. Basically I had a phone object with an animator containing logic and animation clips for “PowerOff” and “PowerOn”. The goal was to play either of the animations, only under the condition that the other has finished playing.

When playing an animation I set a local bool allowTogglePower to false. To detect the end of the animation I used an AnimationEvent through the animation editor attaching a function which sets allowTogglePower to true.

Something like:

public class PhoneEx : MonoBehaviour
{
    public Animator anim;
    bool allowTogglePower = true;
        
    // Called by UI Button Input
    public void ToggleAnimation() 
    {
        if(anim.GetBool("PowerOn") && allowTogglePower)
        {
            anim.SetBool("PowerOn", false);  // Plays OffAnim
            allowTogglePower = false;
        }
        else if(allowTogglePower)
        {
            anim.SetBool("PowerOn", true);   // Plays OnAnim
            allowTogglePower = false;
        }
    }
    
    // Called by AnimationEvent on last frame of each animation
    public void SetToggleOk()
    {
        allowTogglePower = true;
    }
}

It is worth noting that I also set anim.enabled to false in SetToggleOk(). I then set it back to true when playing an animation… Might not be practical for big animator systems, but I don’t like how the animator iterates through the frames of an animation, even though it has finished and looping is turned off.
,

I know this is an old topic, but it is a relevant one. Just hoping to help some people out.

@bigrobizzle your answer was the most helpful. Events are also my choise. for more about Animation events: http://docs.unity3d.com/Manual/AnimationEventsOnImportedClips.html http://docs.unity3d.com/Manual/animeditor-AnimationEvents.html

The following code will help you.
The method returns true if the playing of an animation state on animator is still playing.,
otherwise returns false.
If the layer is “Base Layer” in the Animator View, you should use 0 for the animLayer.

bool isAnimationStatePlaying(Animator anim, int animLayer, string stateName)
{
    if (anim.GetCurrentAnimatorStateInfo(animLayer).IsName(stateName) &&
            anim.GetCurrentAnimatorStateInfo(animLayer).normalizedTime < 1.0f)
        return true;
    else
        return false;
}

@alok-kr-029 i just simply your solution.

just send your animation name in CurrentAnimation you want to check.

 public IEnumerator CheckAnimationCompleted(string CurrentAnim, Action Oncomplete)
    {
        while (!Animator.GetCurrentAnimatorStateInfo(0).IsName(CurrentAnim))
            yield return null;
        if (Oncomplete != null)
            Oncomplete();
    }

calling coroutine

 StartCoroutine(CheckAnimationCompleted("Shoot", () =>
         {
             Animator.SetBool("Shoot", false);
             // Your any code
         }
        ));

Please don't mark comments/follow-ups as (yet another) Answer as you've done here. Instead, mark the Answer you received as correct. I changed this one to a comment for you.

Its old post But I found a better solution

public static IEnumerator CheckAnimationCompleted(string toPlayAnim, string endAnim, Action Oncomplete)
    {
        LevelManager.instance.playerAnimRef.animCntroller.SetTrigger(toPlayAnim);
        while (LevelManager.instance.playerAnimRef.animCntroller.GetCurrentAnimatorStateInfo(0).IsName(endAnim))
            yield return null;

        while (!LevelManager.instance.playerAnimRef.animCntroller.GetCurrentAnimatorStateInfo(0).IsName(endAnim))
            yield return null;

        if (Oncomplete != null)
            Oncomplete();
    }

How To call the function

StartCoroutine(
Utilities.CheckAnimationCompleted(“PickObject”, “Idle”, () =>
{ playerRef.CanTakePlayerInput = true; } ));

// to check if an animation has finished playing

Animator anim;

void Start ()
{
  anim = GetComponent();
}

void Update ()
{
  if (anim.GetCurrentAnimatorStateInfo (0).length < anim.GetCurrentAnimatorStateInfo(0).normalizedTime)
  {
    Debug.Log("animation has finished!!");
  }
}

I have tried using:

this.animator.GetCurrentAnimatorStateInfo(0).IsName("YourAnimationName")

Unfortunately, it’s not working for me. Because it’s always true. Maybe it’s caused by my animator setting as the picture.

However, I figure out another way(by checking its elapsed time and overall duration) and working. Here it is:

var animDuration = animator.GetCurrentAnimatorStateInfo(0).length;
var elapsedTime = animator.GetCurrentAnimatorStateInfo(0).normalizedTime;
var bFinished = (elapsedTime > animDuration/2f);

If you do not have any transitions and would like to to be notified when the animation has ended for the "stateName" in layer 0, I did by calling the following IEnumerator:

public IEnumerator PlayAndWaitForAnim(Animator targetAnim, string stateName)
{
    targetAnim.Play(stateName);
  
    //Wait until we enter the current state
    while (!targetAnim.GetCurrentAnimatorStateInfo(0).IsName(stateName))
    {
        yield return null;
    }
  
    //Now, Wait until the current state is done playing
    while ((targetAnim.GetCurrentAnimatorStateInfo(0).normalizedTime) % 1 < 0.99f)
    {
        yield return null;
    }
  
    //Done playing. Do something below!
    EndStepEvent();
}

The main logic is once the state is entered, we should check if the fractional part of normalizedTime variable reached 1, which means the animation has reached its end state.
Hope this helps

My solution. It plays "attack" animation, then returns to default "stand".

("stand" is looped, "attack" is not. no state triggers used)

if (Input.GetMouseButtonDown(1))
{
    an.Play("Base.pl1-atk");
}
if (an.GetCurrentAnimatorStateInfo(0).IsName("Base.pl1-atk"))
{           
    if (an.GetCurrentAnimatorStateInfo(0).normalizedTime > 1)
    {
        an.Play("Base.pl1-stand");
    }
}

This work perfectly for me:

bool AnimatorIsPlaying(string stateName)
{
    return !AnimatorIsPlaying() && animator.GetCurrentAnimatorStateInfo(0).IsName(stateName);
}

bool AnimatorIsPlaying()
{
    return animator.GetCurrentAnimatorStateInfo(0).normalizedTime < 1;
}

I tried to use is with different setting and it still work.

return !AnimatorIsPlaying() && animator.GetCurrentAnimatorStateInfo(0).IsName(stateName);

Don’t remove ! from start. When you use { animator.GetCurrentAnimatorStateInfo(0).IsName(stateName); } in Start it return false but after 1 or 2 frame it will return true that’s what was causing issue. Just try it and see the result.

if (Input.GetKey(KeyCode.Mouse0))
{
    m_animator.SetBool("attack", true);
    ////si il etait dejas entrain de la jouer faite le sortir de la boucle avant de relancer pour etre en mesure de savoir quand esque le joueur relance sa boucle
    if (m_animator.GetCurrentAnimatorClipInfo(0)[0].clip.name == "MainAttack")
    { m_animator.SetBool("attack", false); }
    else
    { 
        /// debue d'une nouvelle boucle
    }
}
else
{
    m_animator.SetBool("attack", false);
}