Usage

There are several different ways of setting up Animancer Events:

Approach Description Benefits
Transitions Configure the event details in the Inspector. Easily adjust the event times in the Inspector and using the Transition Preview Window to align them correctly with the animation's visuals.
Code Configure the event details in scripts. Keeping all your game logic in scripts helps avoid bugs and create reliable systems.
Hybrid Configure the event times in the Inspector and their callbacks in scripts. Allows non-programmers to adjust event times while allowing programmers to write reliable scripts.

Transitions

Transitions allow you to configure Animancer Events in the Inspector:

  • The Transition Preview Window can be useful to see what the animated character will look like when the event occurs.
  • A transition defines how it fades in but not how it fades out (because that gets determined by the next transition), so the Inspector timeline display simply uses a default value. If the end time is less than the animation length, it shows the fade out ending at the end of the animation. Or if the end time is greater than the length, it shows the default 0.25 second fade.
  • Event Time Fields are always serialized as normalized time, regardless of which field you use to enter the value.
  • Shared Events page explains how to avoid common issues caused by sharing a transition between multiple characters.
  • By default, UnityEvents are used to configure Animancer Events in the Inspector, however there are significant limitations in what they can do so the UltEvents page explains how you can make it use that system instead.

Controls

  • You can select and edit events one at a time, or you can click the foldout arrow next to the timeline to instead show all of the events at once.
  • Double Click in the timeline to add an event at that time.
  • With an event selected:
    • Left and Right Arrows nudge the event time one pixel to the side.
    • Holding Shift moves 10 pixels at a time.
    • Space rounds off the event time by one digit. For example: 0.123 would become 0.12 and 0.999 would become 1.

Code

Animancer Events can also be configured using code like so:

// A Melee Attack script:

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

public void Attack()
{
    // Play clears all events from all states (see Clear Automatically on the Behaviour page for details).
    var state = _Animancer.Play(_Animation);

    // Call OnHitStart to activate the hit box when the animation passes 40% of its length.
    state.Events.Add(0.4f, OnHitStart);

    // Call OnHitEnd to deactivate the hit box when the animation passes 60% of its length.
    state.Events.Add(0.6f, OnHitEnd);

    // Return to Idle when the animation finishes.
    state.Events.OnEnd = EnterIdleState;
}

See End Events for details about how the OnEnd event is different from others.

You can use Lambda Expressions and Anonymous Methods to define the code you want an event to run without needing to create another separate method:

void PlayAnimation(AnimancerComponent animancer, AnimationClip clip)
{
    var state = animancer.Play(clip);

    // All of the following options are functionally identical:

    // Lambda expression:
    state.Events.OnEnd = () =>
    {
        Debug.Log(clip.name + " ended");
    };

    // One-line Lambda expression:
    state.Events.OnEnd = () => Debug.Log(clip.name + " ended");

    // Anonymous method:
    state.Events.OnEnd = delegate()
    {
        Debug.Log(clip.name + " ended");
    };
}

AnimancerEvent.CurrentEvent and AnimancerEvent.CurrentState allow you to access the event currently being triggered and the state triggering it as demonstrated in the Event Utilities example.

Hybrid

Setting up Events in Transitions is usually the best way to configure their timing according to the animation, but setting up Events in Code is often better for creating reliable scripts and avoiding bugs. Fortunately, you can have the best of both approaches by using a Transition to configure the Time but simply leaving its Callback blank and assigning it in Code instead.

You could access the event by index:

_GolfSwing.Events.SetCallback(0, HitBall);

But that hard-codes the assumption that there will not be any other events before the one you want so it may cause a bug later on if you add more events to it. It also means there's nothing in the Inspector to indicate what the event actually is, which makes it harder to maintain complex systems.

A more reliable way to set up a hybrid event is to give it a Name in the Inspector so that you can identify it in code):

[SerializeField] private ClipTransition _GolfSwing;

private void Awake()
{
    // Set the event named "Hit" to call the HitBall method when it is triggered.
    _GolfSwing.Events.SetCallback("Hit", HitBall);
}

private void HitBall() { ... }

The Golf Events example demonstrates how to use this feature.

Event Names Attribute

The EventNamesAttribute allows you to provide a specific set of names that events are allowed have, replacing the usual text field with a dropdown menu to avoid needing to type same event name in the script and in the Inspector.

private const string HitEventName = "Hit";

[SerializeField]
[EventNames(HitEventName)]
private ClipTransition _Swing;

private void Awake()
{
    _GolfSwing.Events.SetCallback(HitEventName, HitBall);
}

This helps avoid spelling mistakes and makes it clearer what the script actually expects to be set up in the Inspector without needing you to actually go and read the script.

Without Attribute With Attribute

Note that values selected using the dropdown menu are still stored as strings. Modifying the names in the script will NOT automatically update any values previously set in the Inspector.

The API documentation for the EventNamesAttribute gives more details about the various different ways it can be used.