Play and wait for Animation/Animator to finish playing
In my script i did that when the player is on the top of the platform move it up. It's working fine. But now i want to make that once it got up play the clip "Down".
using UnityEngine;
using System.Collections;
using System.Reflection;
public class DetectPlayer : MonoBehaviour {
GameObject target;
public void ClearLog()
{
var assembly = Assembly.GetAssembly(typeof(UnityEditor.ActiveEditorTracker));
var type = assembly.GetType("UnityEditorInternal.LogEntries");
var method = type.GetMethod("Clear");
method.Invoke(new object(), null);
}
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.name == "Platform")
{
Debug.Log("Touching Platform");
}
}
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");
target.GetComponent<Animation>().Play("Up");
}
}
}
After the line
target.GetComponent<Animation>().Play("Up");
I want when it finish playing it play the down:
target.GetComponent<Animation>().Play("Down");
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");
}
}
One way to do this without needing to check manually at all is to use queuing
target.GetComponent<Animation>().PlayQueued("Down", QueueMode.CompleteOthers);
This code will wait for any other animation currently playing on the object to finish before playing the queued animation.
The Unity API page regarding this topic