Quick answer: Move the event a few frames before the clip end — the exact last frame can be skipped when the state transitions out. For end-of-clip logic, use a state behaviour’s OnStateExit.
An attack clip has an Animation Event on its final frame to re-enable input. On non-looping clips it sometimes never fires — the state transitioned away before the last frame sampled.
Why the Last Frame Is Unreliable
When a non-looping state finishes, the Animator may transition out before sampling the exact final frame. An event sitting on frame N (the last) can be skipped; an event a few frames earlier always fires.
Move the Event Earlier
Place the event ~2–5 frames before the end. The animation visually finishes the same; the event reliably triggers. For “re-enable input”, slightly early is usually fine anyway.
Use a StateMachineBehaviour
public class AttackEndBehaviour : StateMachineBehaviour
{
public override void OnStateExit(Animator a, AnimatorStateInfo s, int layer)
{
a.GetComponent<PlayerInput>()?.Enable();
}
}
OnStateExit fires reliably whenever the state ends — including via interruption. Best for “always run this when the attack stops” logic.
Loop-Clip Caveat
On looping clips, an event exactly at the loop point can double-fire or skip depending on wrap. Same fix: nudge it slightly off the boundary.
Verifying
Play the attack to completion many times — the end event (or OnStateExit) fires every time. Interrupting the attack still re-enables input via OnStateExit.
“The exact last frame is unreliable. Nudge events inward, or use OnStateExit for end-of-state logic.”
OnStateExit is the robust choice for ‘cleanup when this animation stops’ — it survives interruptions that an end-frame event never sees.