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

WaitForState Coroutine

Discussion in 'Animation' started by Matt1988, Aug 29, 2015.

  1. Matt1988

    Matt1988

    Joined:
    Sep 14, 2012
    Posts:
    23
    I hacked together a pseudo YieldInstruction to wait for the current or specified state to complete.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public static class WaitForState
    6. {
    7.  
    8.     /// <summary>
    9.     /// Wait until specified state, int "hash" state on optional "layer", has started and finished.
    10.     /// </summary>
    11.     public static Coroutine Execute(Animator animator, int stateHash, int layer = 0)
    12.     {
    13.         return animator.GetComponent<CharacterAnimator>().StartCoroutine(StartCoroutine(animator, stateHash, layer));
    14.     }
    15.  
    16.     /// <summary>
    17.     /// Wait until specified state, string "stateName" on optional "layer", has started and finished.
    18.     /// </summary>
    19.     public static Coroutine Execute(Animator animator, string stateName, int layer = 0)
    20.     {
    21.         return Execute(animator, Animator.StringToHash(stateName), layer);
    22.     }
    23.  
    24.  
    25.     private static IEnumerator StartCoroutine(Animator animator, int stateHash, int layer)
    26.     {
    27.         //Wait for our state to start.
    28.         while (animator.GetCurrentAnimatorStateInfo(layer).shortNameHash != stateHash)
    29.         {
    30.  
    31.             yield return null;
    32.         }
    33.  
    34.         //Wait for our state to end.
    35.         while (animator.GetCurrentAnimatorStateInfo(layer).shortNameHash == stateHash)
    36.         {
    37.             yield return null;
    38.         }
    39.     }
    40. }
    41.  
    Here is an example of how you might use it

    Code (csharp):
    1.  
    2. void Update()
    3. {
    4.     if(Input.GetButtonDown("Fire"))
    5.     {
    6.         StartCoroutine("Attack");
    7.     }
    8. }
    9.  
    10. IEnumerator Attack()
    11. {
    12.     animator.SetBool("Attack", true);
    13.     yield return WaitForState.Execute(animator, "AttackState");
    14.     animator.SetBool("Attack", false);
    15. }
    16.  
    So what's going on in our example?

    We check to see if the fire button has been pressed and if it has, we start the Coroutine Attack().
    In our Coroutine, we set the "Attack" parameter to true, trigging our transition into the "AttackState". "yield return WaitForState.Execute(animator, "AttackState")" will tell the Coroutine to continue when the state has ended.

    The reason we provide a state name in Execute is because typically Mecanim doesn't transition to a new state as soon as you set a parameter so we need to poll GetCurrentAnimatorStateInfo until our state has started, then we wait for it to end.
     
    Last edited: Aug 29, 2015
  2. Mecanim-Dev

    Mecanim-Dev

    Unity Technologies

    Joined:
    Nov 26, 2012
    Posts:
    1,675
  3. Matt1988

    Matt1988

    Joined:
    Sep 14, 2012
    Posts:
    23
    So really I guess I could just replace all of the code above with:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class AttackBehaviour : StateMachineBehaviour
    5. {
    6.     [SerializeField] string methodName;
    7.     public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    8.     {
    9.         animator.SendMessage(methodName, SendMessageOptions.DontRequireReceiver);
    10.     }
    11. }
    12.