Shared Events

Every object that plays a particular Transition will share the same AnimancerEvent.Sequence, meaning that any modifications made by one object will affect the others as well. Animancer tries to detect when this happens and will log OptionalWarning.DuplicateEvent if you add the same event to a sequence multiple times with a link leading here so you can try any of the following workarounds.

UnShared

If you're using Transition Assets to share the same transition among multiple objects, you can reference them using an UnShared field.

Instantiate or Clone

If you're using Transition Assets, you can Instantiate a copy of them on startup:

[SerializeField] private ClipTransitionAsset _Animation;

private void Awake()
{
    _Animation = Instantiate(_Animation);
    // Now you can modify the _Animation.Transition.Events as necessary.
}

Or if you're using regular Transitions, you can Clone a copy of them on startup:

[SerializeField] private ClipTransition _Animation;

private void Awake()
{
    _Animation = _Animation.Clone();
    // Now you can modify the _Animation.Events as necessary.
}

That gives each object a separate copy of the transition so they no longer share the same AnimancerEvent.Sequence, but wastes some additional processing time and memory because all the other transition data gets duplicated as well.

This approach also works for custom ScriptableObject types (where UnShared fields only work for Transition Assets).

Manual Copy

A more efficient (but unfortunately more verbose) solution is to make your own copy of the AnimancerEvent.Sequence for each object:

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

private AnimancerEvent.Sequence _Events;

private void Awake()
{
    // Initialize your events by copying the sequence from the transition.
    _Events = new AnimancerEvent.Sequence(_Animation.Events);
    // Now you can modify the _Events as necessary.
}

private void PlayAnimation()
{
    // Then when you play it, just replace the transition events with your own:
    var state = _Animancer.Play(_Animation);
    state.Events = _Events;
}

Unfortunately, this requires you to write and maintain quite a bit more code, but it gives better performance since it only involves duplicating the events for each object rather than the whole transition.

In the Future

The upcoming Animancer v8.0 has a new system for binding event names to callbacks in a centralised dictionary. That would avoid these sharing issues as well since each character has their own dictionary and you wonldn't be touching the events on the shared transitions, they would simply look up the appropriate callback in the current character's dictionary based on their event name.