- Home /
Animation events not firing
I'm having a problem using animation events for a looping 2D sprite animation. While the events usually fire, they don't always fire.
For example, I have a sprite that fires "StartCombatAnimation()" on frame 1, and "EndCombatAnimation()" on the 2nd last frame. That end event fires only about 98% of the time. It fired even less when I had it on the last frame, which is why I moved it up a bit.
As I increase Time.timeScale (ie. say 5x normal time), it misses that event even more, leading me to believe that the frames are being skipped, and therefore the events are being skipped.
I'm looking for advice for a way to reliably force events to fire at the beginning, during, and/or at the end of an animation?
Since it seems like animation events are skipped when frames are skipped and everyone dislikes this, please take the time to vote on this suggestion to add a feature which will allow us to choose to make our animation events guaranteed:
https://feedback.unity3d.com/suggestions/animation-event-guarantee
Answer by b1gry4n · Oct 11, 2014 at 01:57 AM
I dont like relying too much on animation driven events just because they can sometimes hiccup like this. I've found its much easier to just make your own "control" over the events by using a coroutine.
I am guessing you are triggering the "StartCombatAnimation()" as an attack of some sort.
private bool attacking = false;
public float animLength = 1.5f; // adjust this to fit your attack animations length
void Update ()
{
if(Input.GetButtonDown("Fire1") && !attacking )
{
Attack();
}
}
void Attack(){
attacking = true;
//Trigger the animation here
//Trigger the start animation events here
StartCoroutine(Attacking());
}
IEnumerator Attacking(){
yield return new WaitForSeconds(animLength );
// trigger the stop animation events here
attacking = false;
}
To better understand why this is possibly happening it would require us to see the code that triggers your attacks. If you are crossfading or blending animations into one another its most likely the frame that triggers your event is getting cut out.
Thanks, I'll give the coroutine a shot. Regarding my code, I'm not crossfading or anything, it's just looping the animation and not firing the events consistently.
Since it seems like animation events are skipped when frames are skipped and everyone dislikes this, please take the time to vote on this suggestion to add a feature which will allow us to choose to make our animation events guaranteed:
https://feedback.unity3d.com/suggestions/animation-event-guarantee
What I ended up doing, and it seems to be working (*fingers crossed) is adding multiple of the same events on the same animation, so that if it missed the first, surely it would hit the second or 3rd. This will only work in some cases, like my case which was using the event to make a bool value false, and it didn't particularly matter WHEN, just as long as it DID. So far the event seems to be firing consistently now.
I think it is wise to stop the coroutine after starting it. I think it can be done right after the trigger event.
Answer by sampenguin · Sep 04, 2015 at 06:14 AM
Related: I had this problem on events not firing on last frame of a single shot (i.e. not looping) animation. I could fix it by moving the event a few frames forward, but the engineer in me did not like the arbitrary nature and guessed it might be intermittent behavior due to framerate. So I kept digging until I found reliable behavior.
In my case, in the state machine, I had my animation state transitioning to Exit. Apparently this causes some kind of blending to happen (even though I have no other animations or layers in this case, and no explicit blending), which can skip over n number of frames at the end. I deleted the transition to Exit in Animator, and now my Event fires 100% of the time on the final frame.
So I'm guessing any kind of blending activation that occurs has the possibility to drop frames, and if there's an AnimationEvent on a frame that's dropped... guess what, that AnimationEvent doesn't get triggered!
Hope that helps someone else!
This should be categorized as a bug, but judging from how old this issue is from the posts I have seen about it, I doubt it will be fixed as such. If it's by design, it's a poor choice to have the possibility of AnimationEvents getting missed due to blending... these should be guaranteed to hit.
This answer is correct. The way I fixed it was in the Animator of the transition back into the default state (where Has Exit Time is checked), I set the Exit Time slightly longer than the actual animation itself. ie my animation was 1 second so the Exit Time was set to 1.1 seconds
Answer by Deepscorn · Sep 14, 2015 at 10:33 AM
public class CombatBehaviour : StateMachineBehaviour {
// OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
StartCombatAnimation();
}
// OnStateExit is called when a transition ends and the state machine finishes evaluating this state
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
EndCombatAnimation();
}
}
See http://docs.unity3d.com/ScriptReference/StateMachineBehaviour.html for more. That behavior must be added as a component to animator state with your animation. That works 100 % of the time
Yeah, but the difficulty with this is that state behaviours can't reference any properties in the scene — even on the object the state machine is attached to. This is because the state machine actually lives in the project, not in the scene at all. This can make it a PITA to use compared to animation events.
(But, I guess it's the best work-around given the unreliability of animation events!)
You can reference the object it's running on actually. 'animator' references the Animator on the GameObject that's running the current animation.
An example from one of my State$$anonymous$$achineBehaviour classes:
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
humanoidController = animator.GetComponent<HumanoidController>();
}
From there on I can call a public function humanoidController.SwingingSword() or something similar.
Yes, you can certainly do this in code. I only meant that you can't reference scene objects/properties in the inspector.