Problem with Hash IDs & End of animation detection

Hello everyone,

I would like to detect the end of attack animation.

First of all, I tried to use Animation Events but they don’t work (sometimes when animation was very fast the event was not triggered).

I decided to create a custom method for detect an animation changed :

   private void CheckEndOfAnimation()
    {
        var state = CharAnimator.GetCurrentAnimatorStateInfo(0);
        if (Attacking)
        {
            if (!_checkEndAnim)
            {
                if (_currentAttackState != state.nameHash)
                {
                    _currentAttackState = state.nameHash;
                    _checkEndAnim = true;
                }
            }
            else
            {
                if (_currentAttackState == state.nameHash)
                {
                    if (state.normalizedTime >= 0.99f && !CharAnimator.IsInTransition(0))
                    {
                        if (_currentAttackTrail != null)
                        {
                            _currentAttackTrail.transform.parent = null;
                        }
                        Attacking = false;
                        _checkEndAnim = false;
                    }
                }
                else
                {
                    if (_currentAttackTrail != null)
                    {
                        _currentAttackTrail.transform.parent = null;
                    }
                    Attacking = false;
                    _checkEndAnim = false;
                }
            }
        }
        else
        {
            _currentAttackState = state.nameHash;
        }
    }

It seems to work for most cases… but sometimes “state” variable (CharAnimator.GetCurrentAnimatorStateInfo(0)) change in the middle of an animation (without reason).

For debug this problem, I would like to know what is the new animation… but i can’t get the state name.

I tried to instantiate (in Start method) a dictionnary with state name and hash ids for i can’t get the hash (wich is in substate system).

// Works

AnimHashes.Add(Animator.StringToHash("Base.Idle"), "Idle");


// Doesn't work

        AnimHashes.Add(Animator.StringToHash("Base.RightWeakAttack2"), "RightWeakAttack2");
      AnimHashes.Add(Animator.StringToHash("Base.Attacks.WeakAttack.RightWeakAttack2"), "RightWeakAttack2_TEST");

Thanks for your help,

Best regards.

After spent the whole day to debug I found a solution :

Firstly, I found how get an hash id of animation clip (inside substate of substate animator).

Base => Attacks (substate) => WeakAttack (substate) => RightWeakAttack1 (selected animation)

I think the documentation is not complete enough and this page should be updated :

You must use :

Animator.StringToHash("WeakAttack.RightWeakAttack2")

Secondly, I forgot a condition to check the end of animation : Verify that the trigger animation boolean equals false.

I give the solution here for those who have the same problem

Best regards,

@RemiR here is issues for that. Vote please
Unity Issue Tracker - [Animator] Animator.GotoState: State could not be found and
Unity Issue Tracker - Animator.HasState does not properly return TRUE for Nested Substates

Old topic, but if it helps, I did a simple method to get all the clips from a substate given in paremeter :

	/// <summary> Get all the clips from a subState from the base layer </summary>
	private List<AnimationClip> GetClips(Animator animator, string subState) {
		List<AnimationClip> clips = new List<AnimationClip>();
		AnimatorController ac = animator.runtimeAnimatorController as AnimatorController;
		ChildAnimatorStateMachine[] states = ac.layers[0].stateMachine.stateMachines;
		foreach (ChildAnimatorStateMachine state in states) {
			// Debug.Log("State name: " + state.stateMachine.name);
			if (state.stateMachine.name == subState) {
				foreach (ChildAnimatorState childState in state.stateMachine.states) {
					// Debug.Log("Adding clip: " + childState.state.motion.name);
					clips.Add(childState.state.motion as AnimationClip);
				}
			}
		}
		return clips;
	}