Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

How to check if Animator is playing?

Discussion in 'Scripting' started by Deleted User, Dec 10, 2015.

  1. Deleted User

    Deleted User

    Guest

    Hey guys so I’m up to the stage of making jumpscares its almost fully complete. The only thing I need is to check if the Animator not animation is playing, so when its done it returns to the main menu, but can’t get it to work.

    Also you don’t need to read any of the code, just I need to add the code at the bottom.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. public class flashlight : MonoBehaviour {
    4. //checks to see if flash light is on
    5. bool isOn = true;
    6. //the amount of battery life
    7. public float flashlightTimer = 55;
    8. //component for turning on flash light and off sound
    9. public AudioSource FlashLightAudio;
    10. private bool numberOfMonster = false;
    11. //10 seconds after the flashlight is off, make the monster pop up
    12. public float activateMonster = 2.0f;
    13. //this will disable the light on flashlight in the trigmonster script
    14. //this will trigger the boom effect
    15. public AudioSource boomSoundEffect;
    16. //the amount of seconds before the monster pops up
    17. public float monsterTimerPopup = 0.0f;
    18. //used to play the monster sound effect
    19. public AudioSource monsterClip;
    20. public GameObject monsterObject;
    21. public GameObject playMonsterAnim;
    22. public bool MonsterScareSound = false;
    23. void Start()
    24. {
    25. Cursor.visible = false;
    26. //disable monster at start of game
    27. monsterObject.GetComponentInChildren<SkinnedMeshRenderer>().enabled = false;
    28. }
    29. // Update is called once per frame
    30. void Update()
    31. {
    32. //if the user pressed E on the keyboard
    33. if (Input.GetKeyDown(KeyCode.E))
    34. {
    35. FlashLightAudio.Play();
    36. GetComponent<Light>().enabled = false;
    37. //bolean isOn is not true
    38. isOn = !isOn;
    39. }
    40. //if boolean is true then lights are on and if the flash light timer is greater than 0 then minus the time by the delta time
    41. if (isOn == true)
    42. {
    43. if (flashlightTimer > 0)
    44. {
    45. GetComponent<Light>().enabled = true;
    46. flashlightTimer = flashlightTimer - Time.deltaTime;
    47. }
    48. else
    49. {
    50. GetComponent<Light>().enabled = false;
    51. }
    52. }
    53. //if the time is less than 0
    54. if(flashlightTimer < 1 && activateMonster > 0)
    55. {
    56. //set timer to 0
    57. flashlightTimer = 0;
    58. GetComponent<flashlightOffScare>().enabled = false;
    59. //start countdown which will make the monster popup
    60. activateMonster -= Time.deltaTime;
    61. }
    62. if (activateMonster < 1 && numberOfMonster == false)
    63. {
    64. boomSoundEffect.Play();
    65. activateMonster = 0;
    66. numberOfMonster = true;
    67. GetComponent<Light>().enabled = false;
    68. }
    69. //if the boom sound is playing
    70. if (boomSoundEffect.isPlaying == true)
    71. {
    72. //start the monster timer
    73. monsterTimerPopup++;
    74. //if the timer is bigger than 1000
    75. if(monsterTimerPopup > 1000)
    76. {
    77. //set it to 1000
    78. monsterTimerPopup = 1000;
    79. //play boom sound
    80. boomSoundEffect.Play();
    81. if (monsterTimerPopup == 1000 && MonsterScareSound == false)
    82. {
    83. //this is set to one so the light is enabled for a bit longer since in update the light would only last a very small second
    84. flashlightTimer = 1;
    85. //enable the light
    86. GetComponent<Light>().enabled = true;
    87. //make the monster visible which will scare the player
    88. monsterObject.GetComponent<SkinnedMeshRenderer>().enabled = true;
    89. //play the animation creature1roar
    90. playMonsterAnim.GetComponent<Animator>().Play("creature1roar");
    91. //this will play the monster jumpscare sound
    92. monsterClip.Play();
    93. MonsterScareSound = true;
    94. }
    95. }
    96. }
    97. }
    98. }
     
  2. RavenMikal

    RavenMikal

    Joined:
    Oct 18, 2014
    Posts:
    144
    You can try this....
    Code (csharp):
    1.  
    2. PlayMonsterAnim.GetComponent<Animator>().GetCurrentAnimatorStateInfo().TagHash;
    3.  
    4.  
    If I understand correctly, when done playing its value should be at 0.
     
  3. Deleted User

    Deleted User

    Guest

    Didn't work got error also do I put this code at line 94?
     
  4. RavenMikal

    RavenMikal

    Joined:
    Oct 18, 2014
    Posts:
    144
    I was honestly thinking checking it in update once it started, but I've never used it, it was an untested idea because no one was offering anything, with that said, it occurred to me. The first ethan controller script I ever wrote from the Stealth tutorial kept up with which foot was on the ground by timing it with the animation. Ie, if one loop is 2 seconds, the left one hits the floor at second one, the other on second two, etc. So if you know your anims 25 seconds, you could start a timestamp test to see when the 25 seconds have played out, and when it has, execute whatever function.

    Sorry if this is jarbled up, I work 10/4's, today was one of them, and last night I was getting obsessed with my code, and ignored time and common sense. >_<

    Hope it works or one of the 'elders of unity'(said in a dramatic, nibbler voice) show up, but if not when my brain starts working I'll see what I can dig up if no one else has chimed in.
     
  5. Sose

    Sose

    Joined:
    Dec 10, 2015
    Posts:
    27
    I tried checking the API but didn't see any very easy way :D

    What you can do though, is use Animation Events. Simply go to your Animation editor timeline (where you record your animations). Near your other buttons will be a button that looks like a line and a plus symbol. You can hit that to add an event that you can move around the timeline. If you double click on the event on the timeline (it's at the top above all animated properties), you can set it to call a function in one of your script files at that point.



    You need to add a method in one of your scripts that will be called. In my example I named my event Event_CanAttackAgain so my code looks like

    Code (csharp):
    1.  
    2. private void Event_CanAttackAgain()
    3. {
    4.     // This was my test case, you can add anything you need to do after the animation has finished here
    5.     // Like load a new scene etc
    6.     canAttack = true;
    7. }
    8.  
    Note that you may have to add the method to be called before you can find it in your Animation timeline. Hope this helps
     
    RavenMikal likes this.
  6. Deleted User

    Deleted User

    Guest

    Thanks but I already found solution yesterday :) its alot easier.
     
  7. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    ... which was?
     
    Siliko likes this.
  8. Deleted User

    Deleted User

    Guest

    I typed this, also was experimenting its how I found it out

    Code (CSharp):
    1. if(playMonsterAnim.GetComponent<Animator>().isInitialized)
    2. {
    3. StartCoroutine(returnToMenu());
    4. }
    5.  
    6. IEnumerator returnToMenu()
    7. {
    8. yield return new WaitForSeconds(2);
    9. Application.LoadLevel(0);
    10. }
    11.  
     
    lovk4ch likes this.
  9. CloudKid

    CloudKid

    Joined:
    Dec 13, 2015
    Posts:
    207
    For anyone who searches the right solution, after a lot of fail and error the only thing that works is

    Code (CSharp):
    1. yield return new WaitForSeconds(sec);
    or Invoke after sec, where sec is around the lenght of your animation.

    I tried with animation events, what @Sose has described, but please take into account that sometimes the animator skips frames, as it needs to maintains the same speed of animation on all frame rates. In this situations your function will not be called.

    I saw that they are working on a better api for animations, and there will be a good way to tell when the animation will end, but for now just hack it.
     
    lovk4ch likes this.
  10. CloudKid

    CloudKid

    Joined:
    Dec 13, 2015
    Posts:
    207
    I just remembered, there is another way which is reliable and not a hack. On animator states (orange rectangle in animator window) you can actually add code. You class has to inherint StateMachineBehaviour, and then you can override OnStateEnter and OnStateExit, which will be called when your animation goes to another animation (state). This will not work in a loop situation so you will have to make an empty state make transitions to it, and ad code there.
     
  11. Sose

    Sose

    Joined:
    Dec 10, 2015
    Posts:
    27
    Wow, that sounds pretty horrible actually if that actually happens. I wonder what the reasoning behind this is..? Atleast Unity could call the function late if it skips past one
     
  12. CloudKid

    CloudKid

    Joined:
    Dec 13, 2015
    Posts:
    207
    Yea, i'm sure its a bug or something. If you search for "Animation event not firing" you will find a lot of people complaining that the events are not constant. Note that the position of the event matters, and I find that the ones on the end of animations give me trouble. It might also be because of the way transitions work and how the skip some frames of first animations to blend into the second. It happened to me in 2 different situations in which I could not make them consistent so I switched to my above explained method.
     
  13. Deleted User

    Deleted User

    Guest

    Oh damn that is a worry, didn't even know that. What is your solution?
     
  14. Deleted User

    Deleted User

    Guest

    Could you please write me a easy solution? Since my game is going to be released in a couple days.
     
  15. CloudKid

    CloudKid

    Joined:
    Dec 13, 2015
    Posts:
    207
    Which one? The hack with corutine, or the one with StateMachineBehaviour?
    The Corutine one is easy:

    Call StartCorutine("WaitForMyAnim"), in the same place where you start your anim / activate your object
    Code (CSharp):
    1. private IEnumerator WaitForMyAnim()
    2. {
    3.         yield return new WaitForSeconds(Amount of seconds my anim is);
    4.  
    5.        //Here you call your function, after the animation ended
    6. }
    If it finishes to fast just add more seconds. If this is not reliable enough, you need to search on google on how to use StateMachineBehaviour, because it hard for me to explain here.
     
  16. Deleted User

    Deleted User

    Guest

    I;m talking about the hack, I want it so its not hacky
     
  17. CloudKid

    CloudKid

    Joined:
    Dec 13, 2015
    Posts:
    207
    What? You can't un-hack the hack... that is why it is called a hack in the first place. You ether use animation events and hope for the best, or use StateMachineBehaviour. For both you have to look online, as those processes imply more then just some code (you have to do some unity editor stuff)
     
  18. Deleted User

    Deleted User

    Guest

    Ok thanks.
     
  19. Thaxxel

    Thaxxel

    Joined:
    Jan 14, 2014
    Posts:
    8
    I know this was two years ago, however there is a better way around using WaitForSeconds, WaitForSecondsRealtime, and another method other than Animation Event Triggers for anybody looking around for an answer to this problem.

    One method is to create an empty state at the end of your animation loop and name it whatever you would like. For my purposes, I just named my empty state "END".

    After playing your animation, in your coroutine that you need to wait until your animation is over type:

    yield return new WaitUntil (()=> 'YourAnimator'.GetCurrentAnimatorStateInfo(0).IsName("END") == true);

    This basically will keep waiting until your Animator has reached the empty play stat named END to continue, or whatever you named your empty animation state after your animation has ended.

    This is a very accurate way of getting something to happen right after your animation is over if you'd rather not use Animation Event Triggers, and is still a better way than guessing the amount of seconds to wait.

    Hope this helps anyone searching around!

    Cheers!
     
    IchorX, carlbinalla, jrock84 and 3 others like this.
  20. jrock84

    jrock84

    Joined:
    Jan 13, 2016
    Posts:
    38
    For anyone beating their head against a wall looking for a good solution like I was, use Thaxxel's method. Just be sure to also uncheck Write Defaults. This resets your animation once the transition takes place to it's default state.
     
  21. JackofTraes

    JackofTraes

    Joined:
    May 28, 2014
    Posts:
    10
    Additionally - if using a finite state machine or anything similar - one could wait for the animation to end (as an exit condition) by accessing the normalizedTime property of the GetCurrentAnimatorStateInfo (AnimatorStateInfo) method.

    For example, I wanted to create an idle timeout state that transitions back to the idle state after the animation plays. I did so like this:

    Code (CSharp):
    1.         // Replace 'Animator' with the variable name for yours.
    2.         if (Animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1) {
    3.             // Do something.
    4.         }
    I am currently using this method for a prototype, but I have additional code in the full script that randomly picks a number (from a predefined range) and plugs it in as an animator float. The animator then uses that float's value to determine which animation to play.

    As you may infer, I would have no way of knowing how long to wait as each animation is of a different length so I have the normalizedTime tell me instead. That solves everything for me while also allowing for the state or state system to scale, retroactively.

    Disclaimer: As implied by my opening statement, please note that this method is NOT intended for code structured like the OP. The code will execute after the current animation is finished playing, but if there are multiple animation states in the animator, there is no way to know beforehand exactly which animation will be the trigger (as all logic is running in the update method at the same time).

    For code structured as such, you would have to modify my snippet by using additional flags or conditionals to make it work properly for your game. Otherwise, you'd probably be better off with Thaxxel's method as you can target specific as opposed to generic states. The preference of either will ultimately be in the hands of the developer(s) during production phase.

    EDIT: After experimenting with different idle timeout delay values, I found out that the normalizedTime property doesn't always return as a normalized value! In these cases, it looks like comparing a timer or dynamic coroutine to the length property will work 100% of the time [so far] as a workaround until that issue is (hopefully) fixed.

    Hope this helps someone!
     
    Last edited: Aug 30, 2019
    Buck1984 likes this.
  22. Nevazhnov

    Nevazhnov

    Joined:
    Jul 11, 2019
    Posts:
    8
    Sorry for necroposting, but I've found one more elegant solution to check if animation done. Launch this coroutine after you set up desirable animation trigger. In this code sample it takes current state's animation lenght (in seconds) of first AnimationClip from zero layer and passes it to WaitForSeconds.
    Code (CSharp):
    1. private IEnumerator WaitAnimationOverAndDoThings()
    2. {
    3.     yield return new WaitForSeconds(myAnimator.GetCurrentAnimatorClipInfo(0)[0].clip.length);
    4.     Debug.Log("Now animation clip is done and we can process further");
    5. }
    Hope it will help someone :)
     
    Last edited: Nov 26, 2020