5
\$\begingroup\$

I'm using the newest version of Unity. I have a turn based RPG type of game so everything is very sequential. The actor jumps in front of another actor, plays his melee attack animation, then jumps back. Currently I use events on the animation to tell when the attack animation is complete. The 2 side effects of doing it this way are:

  1. I have to go into each animation and make an event at the end of them. It would be ideal to NOT have to do this. It would seem I should be able to just check in code if a certain animation is finished playing.

  2. The control flow of my attack code becomes awkward. My attack method is a coroutine so I can read it very sequentially and it makes sense. Having this event to tell when the animation is complete ruins that flow. I have to basically make a bool variable and inside my finished event set it, and in my attack function do a while loop after I play the animation checking for that bool variable to change and yielding out until it does. Requiring this event ruins the logical flow of attacks. If I have many animations for whatever reason things get messier.

I noticed the Animator component doesn't have an isPlaying() like some other answers suggested. I think those answers were for older version of unity and older just playing animation clips not using the new Animator component.

There must be a better way to do this.

\$\endgroup\$
3
\$\begingroup\$

If you have named all of the attacking states identically, you can check the name of the currently playing animation:

//Start animation

//Ensure they are in the "Attacking" state *before* getting to this loop
yield return null;
while (animator.GetCurrentAnimatorStateInfo(0).IsName("Attacking")) {
    yield return null;
}

//Animation has finished

Note: that 0 refers to the layer of animation you want to check. If you happen to be on multiple layers (which seems unlikely to me for this type of animation), make sure you are checking the correct one.

Alternatively, you can set the tag on the relevant states, so that you can use IsTag instead of IsName, and not have to change the name of the states, if that would be a problem.

EDIT

It may be necessary to wait for more than a single frame before GetCurrentAnimatorStateInfo returns the "Attacking" state. In that case, you may wish to replace the first yield return null with its own loop:

//Start animation

//Wait for them to enter the Attacking state
while (!animator.GetCurrentAnimatorStateInfo(0).IsName("Attacking")) {
    yield return null;
}

//Now wait for them to finish
while (animator.GetCurrentAnimatorStateInfo(0).IsName("Attacking")) {
    yield return null;
}
\$\endgroup\$
  • \$\begingroup\$ OK, so IsName() is really checking the state name that's currently playing on that layer? Does that still work while animations are blending out/in? I would think you'd have 2 animations playing on a state for a little while, while that blending is happening? Calling that right after starting the animation (on the same frame) will work? I don't have access yet to try. \$\endgroup\$ – user441521 Oct 12 '17 at 18:51
  • \$\begingroup\$ You can use animator.GetNextAnimatorStateInfo to get the one being blended into during a transition. Checking on the same frame the animation starts would not be advisable, hence the yield return null in the comments. \$\endgroup\$ – Ed Marty Oct 12 '17 at 19:25
  • \$\begingroup\$ I needed the yield return null before the while loop but this works. Thank you! \$\endgroup\$ – user441521 Oct 13 '17 at 2:06
2
\$\begingroup\$

My answer is based on events.... But not on events inside Animators clips!

You have the possibility to attach scripts to Mecanim's states if you want to know when the control flow is entering / leaving your state. For example :

using UnityEngine;
using System.Collections;

public class MyAnimatorState : StateMachineBehaviour
{
    public delegate void MyEvent(bool entering);
    public event MyEventHandlerHandler MyEvent;

    override public void OnStateEnter( Animator animator, AnimatorStateInfo stateInfo, int layerIndex )
    {
        if ( MyEvent != null )
            MyEvent( true );
    }
    override public void OnStateExit( Animator animator, AnimatorStateInfo stateInfo, int layerIndex )
    {
        if ( MyEvent != null )
            MyEvent( false );
    }
}

Then, in a script attached to your gameObject :

public Animator animator ;
private MyAnimatorState animatorState;

private void Start()
{
    // Don't call this line in Awake, it won't work
    animatorState = animator.GetBehaviour<MyAnimatorState>();
    animatorState.MyEvent += ( entering ) => { Debug.Log(entering) ; };
}

Source : https://unity3d.com/fr/learn/tutorials/modules/beginner/5-pre-order-beta/state-machine-behaviours

\$\endgroup\$
  • \$\begingroup\$ So I would have to create a class like this per state? \$\endgroup\$ – user441521 Oct 12 '17 at 23:35
  • \$\begingroup\$ Only on states for which you want to be warned that they end. I concede it's quite fastidious but it's the only alternative to animation events I know. \$\endgroup\$ – Hellium Oct 13 '17 at 5:47
  • \$\begingroup\$ I never really understand that states in animator. I mean from what I can see each state is it's own animation so this would mean I would have to do this for each one which isn't very viable. The other solution here worked and is more generic in nature, but thanks for the alternative. \$\endgroup\$ – user441521 Oct 13 '17 at 12:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.