Alternatives to End Events

End Events are usually the simplest and most performant way to detect and handle the end of an animation, but this page lists several alternative ways to achieve similar behaviour if you can't or don't want to use them.

Coroutines

In coroutines you can yield return any AnimancerState to wait until it finishes. Specifically, it will wait until the state either stops playing or passes its end time (set using code or Transitions as described above).

[SerializeField] private AnimancerComponent _Animancer;
[SerializeField] private AnimationClip _Animation;

void Awake()
{
    StartCoroutine(CoroutinePlayAnimation());
}

IEnumerator CoroutinePlayAnimation()
{
    Debug.Log("Starting animation");

    var state = _Animancer.Play(_Animation);
    yield return state;

    Debug.Log("Animation ended");
}

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

Note that yielding a state does not create Garbage, but starting a coroutine does.

Manual Polling

You can keep a reference to the AnimancerState returned by any of the AnimancerComponent.Play methods and check its AnimancerState.NormalizedTime every update (once the value reaches 1 the animation is finished):

[SerializeField] private AnimancerComponent _Animancer;
[SerializeField] private AnimationClip _Animation;

private AnimancerState _State;

void Awake()
{
    _State = _Animancer.Play(_Animation);
}

void Update()
{
    if (_State.NormalizedTime >= 1)
        Debug.Log("Animation ended");
}

Since states are kept in an internal dictionary, you can also get them quite efficiently when needed:

[SerializeField] private AnimancerComponent _Animancer;
[SerializeField] private AnimationClip _Animation;

void Awake()
{
    _Animancer.Play(_Animation);
}

void Update()
{
    var state = _Animancer.States[_Animation];
    if (state.NormalizedTime >= 1)
        Debug.Log("Animation ended");
}

Note that if a state has not already been created for the animation (because it has not yet been played) then _Animancer.States[_Animation] will throw an exception so you may wish to use _Animancer.States.TryGet(_Animation, out var state) or _Animancer.States.GetOrCreate(_Animation) instead.