Play and Wait for Animation/Animator to Finish Playing

Play and wait for Animation/Animator to finish playing

While both answers should work, another method of doing this with coroutine and the IsPlaying function. You use the coroutine solution if you also want to perform other task after the animation.

For the Animation system:

The old Unity animation playback system. This should not be used in your new Project unless you are still using old Unity version.

IEnumerator playAndWaitForAnim(GameObject target, string clipName)
{
Animation anim = target.GetComponent<Animation>();
anim.Play(clipName);

//Wait until Animation is done Playing
while (anim.IsPlaying(clipName))
{
yield return null;
}

//Done playing. Do something below!
Debug.Log("Done Playing");
}

For the Animator system

This is the new Unity animation playback system. This should be used in your new Project instead of the Animation API. In terms of performance, it's better to use the Animator.StringToHash and compare the current state by hash number instead of the IsName function which compares string since the hash is faster.

Let's say that you have state names called Jump, Move and Look. You get their hashes as below then use the function for playing and waiting for them them below:

const string animBaseLayer = "Base Layer";
int jumpAnimHash = Animator.StringToHash(animBaseLayer + ".Jump");
int moveAnimHash = Animator.StringToHash(animBaseLayer + ".Move");
int lookAnimHash = Animator.StringToHash(animBaseLayer + ".Look");

public IEnumerator PlayAndWaitForAnim(Animator targetAnim, string stateName)
{
//Get hash of animation
int animHash = 0;
if (stateName == "Jump")
animHash = jumpAnimHash;
else if (stateName == "Move")
animHash = moveAnimHash;
else if (stateName == "Look")
animHash = lookAnimHash;

//targetAnim.Play(stateName);
targetAnim.CrossFadeInFixedTime(stateName, 0.6f);

//Wait until we enter the current state
while (targetAnim.GetCurrentAnimatorStateInfo(0).fullPathHash != animHash)
{
yield return null;
}

float counter = 0;
float waitTime = targetAnim.GetCurrentAnimatorStateInfo(0).length;

//Now, Wait until the current state is done playing
while (counter < (waitTime))
{
counter += Time.deltaTime;
yield return null;
}

//Done playing. Do something below!
Debug.Log("Done Playing");

}


For a solution specifically for your particular problem with the collision callback function (OnTriggerEnter), there are two possible ways to do that:

1.Start a coroutine function to play the animation after trigger detection:

void OnTriggerEnter(Collider other)
{
if (other.gameObject.name == "OnTop Detector")
{
Debug.Log("On Top of Platform");
GameObject findGo = GameObject.Find("ThirdPersonController");
GameObject findGo1 = GameObject.Find("Elevator");
findGo.transform.parent = findGo1.transform;
target = GameObject.Find("Elevator");
StartCoroutine(playAnim(target));
}
}

IEnumerator playAnim(GameObject target)
{
Animation anim = target.GetComponent<Animation>();
anim.Play("Up");

//Wait until Up is done Playing the play down
while (anim.IsPlaying("Up"))
{
yield return null;
}

//Now Play Down
anim.Play("Down");
}

OR

2.Make the OnTriggerEnter function a coroutine(IEnumerator) instead of void function:

IEnumerator OnTriggerEnter(Collider other)
{
if (other.gameObject.name == "OnTop Detector")
{
Debug.Log("On Top of Platform");
GameObject findGo = GameObject.Find("ThirdPersonController");
GameObject findGo1 = GameObject.Find("Elevator");
findGo.transform.parent = findGo1.transform;
target = GameObject.Find("Elevator");
Animation anim = target.GetComponent<Animation>();
anim.Play("Up");

//Wait until Up is done Playing the play down
while (anim.IsPlaying("Up"))
{
yield return null;
}

//Now Play Down
anim.Play("Down");
}
}

How to wait until animation is finished when using Animator?

Try this

IEnumerator popDownAnimation(string c)
{
animator = GetComponent<Animator>();
animator.Play("Pop Down Animation");
float animationLength = animator.GetCurrentAnimatorStateInfo(0).length;
yield return new WaitForSecondsRealtime(animationLength);
animator.Play("New State");
}

However, like @NotVeryGoodAtThis mentioned, I wouldn't do this in code unless you have a specific reason why you can't make it work using animation events or using parameters within the animator to transition between animation states. Designing your animation using Unity's Animator window and using states, parameters, and transitions will be easier to manage than doing it through code, especially as your animators become more complicated.

Play animation 5 times

Use the for loop in IEnumerator to solve the problem. Also make sure you enter the layer number correctly. Here the wave state is repeated 5 times in layer zero.

private IEnumerator PlayAnimInterval(int n)
{
for (int i = 0; i < n; i++)
{
anim.Play("wave", 0, 1);
yield return new WaitForSeconds(anim.GetCurrentAnimatorStateInfo(0).length);
}
}

How to detect Animator Layer?

The animator layer is an array. By adding each layer in the layers section, its index is also added. You can find its code below.

Sample Image


Repeat State Info with Behaviour

In this method you can solve the problem of repetition in any state. Just create a behavior like the one below and just add the number of repetitions. This method also works independently of the layer.

Sample Image

Sample Image

public class Repeat : StateMachineBehaviour
{
public int repeatTime;
public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
if (repeatTime <= 0) return;

repeatTime--;
animator.Play(stateInfo.fullPathHash);
}
}

Wait for an animation to finish in unity3d

Basically you need to do two things for this solution to work:

  1. Start the animation.
  2. Wait for the animation to finish before you play next animation.

An example of how this could be done is:

animation.PlayQueued("Something");
yield WaitForAnimation(animation);

And the definition for WaitForAnimation would be:

C#:

private IEnumerator WaitForAnimation (Animation animation)
{
do
{
yield return null;
} while (animation.isPlaying);
}

JS:

function WaitForAnimation (Animation animation)
{
yield; while ( animation.isPlaying ) yield;
}

The do-while loop came from experiments that showed that the animation.isPlaying returns false in the same frame PlayQueued is called.

With a little tinkering you can create a extension method for animation that simplifies this such as:

public static class AnimationExtensions
{
public static IEnumerator WhilePlaying( this Animation animation )
{
do
{
yield return null;
} while ( animation.isPlaying );
}

public static IEnumerator WhilePlaying( this Animation animation,
string animationName )
{
animation.PlayQueued(animationName);
yield return animation.WhilePlaying();
}
}

Finally you can easily use this in code:

IEnumerator Start()
{
yield return animation.WhilePlaying("Something");
}

Source, alternatives and discussion.

How can I wait for the object to end spinning and then play the animation?

In your case you can make your calling IEnumerator wait until the other one is finished by simply yielding it like

...

// executes and at the same time waits for Spin to finish
yield return Spin(3f, rotation);

// called when Spin routine is finished
anim.SetBool("Roll_Anim", true);

Alternatively instead of a third-party tool library like suggested here you could simply add a callback like

IEnumerator Spin(float lerpTime, Quaternion rotation, Action whenDone)
{
float elapsedTime = 0f;

while (elapsedTime <= lerpTime)
{
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, elapsedTime / lerpTime);
elapsedTime += Time.deltaTime;
yield return null;
}

whenDone?.Invoke();
}

and then start the routine either using a lambda expression like

StartCoroutine(Spin(3f, rotation, () => 
{
anim.SetBool("Roll_Anim", true);
}));

or a method call like

private void AfterSpinning()
{
anim.SetBool("Roll_Anim", true);
}

...

StartCoroutine(Spin(3f, rotation, AfterSpinning));

Unity - wait until animation finished

in the animation tab of your animated gameobject create an event at the last frame of the animation, attach the above script to this gameobject, and choose the method you want to run at the end of it

Sample Image

Remove The coroutine and just make a simple method

public void LoadScene()
{
LoadAsync(sceneName, sliderLoadbar, sliderLoadbarText)
}


Related Topics



Leave a reply



Submit