I have a piece of code that want to wait for an animation to complete.
I realize I can try to time the event with Invoke or WaitForSeconds, but since the animation can vary in length I don't find this method that appealing.
I'd like there to be a WaitForAnimation yield instruction so I can easily play code in sequence.
or even shorter like
Are there nice options to synchronize animation completion? (Is there a global list of methods that work with coroutines?)
asked Dec 09 '10 at 11:15 AM
Here is a solution I came up with that waits for the animation to finish. It doesn't work too well in case you have multiple animations queued up from any other source, but as long as you're calling the animations from one single place, this works.
Basically you need to do two things for this solution to wotk;
An example of how this could be done is:
And the definition for WaitForAnimation follows:
The do-while loop came from experimentation that the animation.isPlaying return false the same frame you call PlayQueued for some reason.
With a little tinkering you can create a extension method for animation that simplifies this such as:
Finally you can easily use this in code:
You could try checking to see when the current time of your animation is larger than the length of the animation. The docs say that the animation time goes to infinity because it loops the animation over and over. I am assuming that animation time stops when you pause an animtion. I have not tested this but it would go something like:
or the non-coroutine way
answered Dec 09 '10 at 12:56 PM
A more general solution is to set up a notification at some arbitrary time in the animation cycle, where the end of the cycle is a special case. I've created a class that holds an animation clip, and fires an event at a specified time in the animation cycle. I will present two equivalent implementations below, one that uses class AnimationEvent (which is sort of the native Unity way), and one that uses a Coroutine to measure the animation time against the notification time.
Personally, I prefer using the second technique. Why? Unity uses its messaging system (e.g. SendMessage()) to fire the AnimationEvent. The message is bound only by the name of the method, and the message is sent to all script components on the GameObject. If some other script on your GameObject has a method with the same name, it too will receive the message, which is almost certainly a bug.
You can mitigate the issue by creating a mangled name for your method, but that gets really ugly--and it doesn't even completely solve the problem! Consider if you have two instances of the script component on a single GameObject. (You might do this if you have two different clips that you want to monitor.) Now both components receive the message, regardless of the method name. I've addressed this by adding the script's object ID as a parameter on the AnimationEvent, and then checking the value in the message handler. It works, but it does add complexity.
As for sending the notification to the outside world, you could use SendMessage(), but I prefer using a .NET event. It has several advantages: you get better performance; you avoid the binding issue described above; it is architecturally more elegant, as it insulates the class from being concerned with who's listening for the notification.
So, the prose are nearly complete. :) To use this, add it as a component to the GameObject that gets animated. You can set the clip at design time or at run-time. I've expressed the time to fire the notification as animation frames, but you could just as well use seconds; if the frame is set to zero, I take that to mean at the end of the clip. You attach a handler to the NotifyFrameReached event, and voila! You have notification that you've reached the specified frame!
And without further ado, my preferred Coroutine-based implementation:
public class NotifyingAnimControl_CoroutineBased : MonoBehaviour
And here's the Messaged-based implementation:
using UnityEngine; using System;
public class NotifiyingAnimControl_MessageBased : MonoBehaviour
Even though it's older, this Q&A tends to rise to the top in searches. But fortunately, I dug deeper before going to work. This solution may not have been available with previous builds, but nowadays, you can use the built-in animation events system.
answered May 10 at 05:18 AM