Async and Coroutines

Animancer Events and End Events normally use callback delegates to run your code when the event occurs, but you can also use Async Methods or Coroutines to wait for an event before doing something.

Waiting for the End of an Animation

To wait for an End Event you can await or yield return an AnimancerState directly. For example:

Async Method Coroutine
public class AsyncExample : MonoBehaviour
{
    [SerializeField]
    private AnimancerComponent _Animancer;

    [SerializeField]
    private AnimationClip _Animation;
    
    protected virtual void Awake()
    {
        PlayThenLog();
    }

    public async void PlayThenLog()
    {
        AnimancerState state =
            _Animancer.Play(_Animation);

        await state;
        
        Debug.Log("Finished " + state);
    }
}
public class CoroutineExample : MonoBehaviour
{
    [SerializeField]
    private AnimancerComponent _Animancer;

    [SerializeField]
    private AnimationClip _Animation;
    
    protected virtual void Awake()
    {
        StartCoroutine(PlayThenLog());
    }

    public IEnumerator PlayThenLog()
    {
        AnimancerState state =
            _Animancer.Play(_Animation);

        yield return state;
        
        Debug.Log("Finished " + state);
    }
}

Both of the above scripts achieve the same thing:

  1. On startup, they call the PlayThenLog method (or start it as a coroutine).
  2. That method immediately plays the _Animation which creates an AnimancerState for it.
  3. Then it waits for the animation to end or be interrupted. See When does it stop waiting for details.
  4. Then it logs a message saying it finished.

You can also await or yield an entire Layer or AnimancerComponent to wait for all their states to finish.

Waiting for Time or an Animancer Event

To wait for a specific time or an Animancer Event you can call AnimancerState.Await and await or yield return the Awaiter it returns.

AnimancerState.Await can be given an AnimancerState.NormalizedTime. For example:

public async void PlayUntilTime(float normalizedTime)
{
    AnimancerState state = _Animancer.Play(_Animation);

    await state.Await(normalizedTime);

    Debug.Log("Passed normalized time " + normalizedTime);

    await state;

    Debug.Log("Finished " + state);
}

Or it can be given an event name like when using Animancer Events with the Hybrid approach. For example:

public async void PlayUntilEvent(StringReference eventName)
{
    AnimancerState state = _Animancer.Play(_Animation);

    await state.Await(eventName);

    Debug.Log("Passed event " + eventName);

    await state;

    Debug.Log("Finished " + state);
}

The event name can be a regular string or any of the options explained on the Strings page.

When does it stop waiting?

The above await and yield statements will wait for one of the following conditions:

  1. The state successfully passes the target time.
  1. The state is interrupted by:
  • The state.TargetWeight becoming 0, such as if it starts fading out or is stopped by playing something else.
  • The state is destroyed.

If you want to wait forever until the state is interrupted, you can use state.Await(float.PositiveInfinity).

Awaiting a result

In many situations, you only want to continue executing logic for a state if that state is still playing. For example, if you play an Attack animation and wait for its Hit event to apply damage to another character, then you might not want to apply damage if the animation was interrupted before reaching that event.

The above await statements can also optionally return a result to indicate whether the state successfully reached the target time. For example:

public async void PlayWithResult()
{
    AnimancerState state = _Animancer.Play(_Animation);

    bool success = await state;

    if (success)
    {
        Debug.Log("Finished " + state);
    }
    else
    {
        Debug.Log("Interrupted " + state);
    }
}

The same can be done in a coroutine, but it takes a bit more effort since you need to get the Awaiter before the yield so that you can check if it WasSuccessful afterwards:

public IEnumerator PlayWithResult()
{
    AnimancerState state = _Animancer.Play(_Animation);

    AnimancerState.Awaiter awaiter = state.Await();

    yield return awaiter;

    if (awaiter.WasSuccessful)
    {
        Debug.Log("Finished " + state);
    }
    else
    {
        Debug.Log("Interrupted " + state);
    }
}