Manual Animation Blending

Hello,

I’ve got an issue with Animation Blending. The following code does NOT work if I remove the call to .Stop() on the animation component.

    if( m_Transitioning )
	{
		if( !m_Animator.IsPlaying( m_TransitionalAnimation ) )
		{
			m_Transitioning = false;
			
			// HERE IT DOESN'T WORK...
			m_Animator.Stop();
			m_Animator.Play( "ENY_M_Stand_CBR_AlertIdle" );
			m_Animator[ "ENY_M_Stand_CBR_AimLeft" ].enabled = true;
			m_Animator[ "ENY_M_Stand_CBR_AimCenter" ].enabled = true;
			m_Animator[ "ENY_M_Stand_CBR_AimRight" ].enabled = true;
		}
		
		return;
	}
	
	if( Input.GetKey( KeyCode.H ) )
	{
		m_Animator[ "ENY_M_Stand_CBR_AimLeft" ].weight = 0.0f;
		m_Animator[ "ENY_M_Stand_CBR_AimCenter" ].weight = 0.0f;
		m_Animator[ "ENY_M_Stand_CBR_AimRight" ].weight = 1.0f;
	}
	else if( Input.GetKey( KeyCode.J ) )
	{
		m_Animator[ "ENY_M_Stand_CBR_AimLeft" ].weight = 0.0f;
		m_Animator[ "ENY_M_Stand_CBR_AimCenter" ].weight = 0.5f;
		m_Animator[ "ENY_M_Stand_CBR_AimRight" ].weight = 0.5f;
	}

This is called inside an update each frame. I first CrossFade() a transitional animation (an animation of the enemy being alerted to the player’s presence). After that animation is done, I have to call .Stop() before I can play the idle animation, otherwise my manual blending of the weights to have the enemy face the player does NOT work.

Ideally, I’d like to be able to call CrossFadeQueued() on the AlertIdle animation, but since I seemingly have to call Stop before I enable my manual weights, I see no way around straight up calling .Play().

Also, I have the layer of those aiming animations set to 1 (rest should still be 0).

Thanks for the help, I really need/appreciate it.
-Matt

Is your ENY_M_Stand_CBR_AlertIdle animation also on layer 1? If so, what’s the wrapmode of this animation? I guess it should be once since you want to set your blend weights manually. Keep in mind that the weights always get normalized. If there are multiple animations with a weight > 0 the final weights are distributed proportional to their weights.

So in case ENY_M_Stand_CBR_AlertIdle is still running it has a weight of 1.0f. Now when you set the weight of ENY_M_Stand_CBR_AimRight also to 1.0f the resulting weights will be:

ENY_M_Stand_CBR_AlertIdle = 0.5f
ENY_M_Stand_CBR_AimRight = 0.5f

And in the second case:

ENY_M_Stand_CBR_AlertIdle = 0.66666   (1.0)
ENY_M_Stand_CBR_AimCenter = 0.33333   (0.5)
ENY_M_Stand_CBR_AimRight = 0.33333    (0.5)

edit

Try this debug script. Just attach it to any gameobject (can also be done at runtime) and drag the desired animation component / gameobject onto the animation variable.
Press F12 to open the debug window in game. Now you can watch your animations in realtime. Maybe there’s another one active on the same layer?

I always create my debug tools to run also at runtime so they can be used in a build as well as in the editor. Feel free to extend / change it to your needs :wink:

//DebugAnimations.cs
using UnityEngine;
using System.Collections;

public class DebugAnimations : MonoBehaviour
{
    public Animation animation;
    public Rect pos = new Rect(0,0,500,500);
    private Vector2 m_scroll;
    private bool m_Visible = false;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.F12))
            m_Visible = !m_Visible;
    }
    void OnGUI ()
	{
        if (m_Visible)
            pos = GUILayout.Window(12345,pos,OnWindow,"Animations");	
	}
    void OnWindow(int id)
    {
        GUI.DragWindow(new Rect(0,0,10000,15));
        if (animation == null)
            return;
        
        m_scroll = GUILayout.BeginScrollView(m_scroll);
        GUILayout.BeginHorizontal();
        GUILayout.Label("Name");
        GUILayout.Label("active", GUILayout.Width(50));
        GUILayout.Label("weight", GUILayout.Width(50));
        GUILayout.Label("layer", GUILayout.Width(50));
        GUILayout.Label("wrapmode", GUILayout.Width(100));
        GUILayout.EndHorizontal();
        foreach (AnimationState S in animation)
        {
            GUILayout.BeginHorizontal();
            GUILayout.Label(S.name);
            GUILayout.Label(""+S.enabled,GUILayout.Width(50));
            GUILayout.Label(S.weight.ToString("0.00"),GUILayout.Width(50));
            GUILayout.Label(""+S.layer,GUILayout.Width(50));
            GUILayout.Label(""+S.wrapMode,GUILayout.Width(100));
            GUILayout.EndHorizontal();
        }
        GUILayout.EndScrollView();
    }
}
                                Total  2.0 --> get normalized to 1.0

Well, consider my mind blown. I have absolutely no idea why this appears to be working now. I’ve made many changes, none of which I’d expect to affect this, but evidently they have. I still have some strange issues to deal with, hopefully fixing those issues doesn’t reintroduce this problem.

Thanks again for attach that script, it helped me visualize exactly what’s happening. I did tweak it to only show the active animations since our enemies have dozens of animations (eventually toppling 100 for sure).

My newest problem is fixing my base object so that when the enemy plays a transitional animation that moves him off center, the base object can recalibrate so that the next time I play an animation it’s relative to the new orientation. I’ll create a new question for this though, thanks for the help! :slight_smile: