Using animation blending to create aim poses (3.5)

To summarize, I’m trying to create a side scrolling action game where the player character aims their weapon at the mouse cursor. To accomplish this I’ve created a biped model with idle and run animations, as well as 3 aim poses: Up, Forward and Down. These “aim pose” animations are 2 frames long (since Unity doesn’t seem to allow 1 frame animations).

The model and animations are all in a single FBX, with the animations split up in the Inspect in Unity. I can view each animation in the animation window and they are correct. The problem comes when I try to blend them.

My approach has been to put the aim poses on a higher animation level than the run/idle animations. The idle/run animations are set to loop, and the aim poses are Clamp Forever. I then play all the aim poses at the same time, and adjust their weight based on the angle between the player character and the cursor. Here’s the code:

void Start()
{
	//The "upperBody" transform is an animation node in the spine
	animAimDown = animation["aim_down"];
	animAimDown.layer = 10;
	animAimDown.AddMixingTransform( upperBody );
		
	animAimUp = animation["aim_up"];
	animAimUp.layer = 10;
	animAimUp.AddMixingTransform( upperBody );
		
	animAimForward = animation["aim_forward"];
	animAimForward.layer = 10;
	animAimForward.AddMixingTransform( upperBody );
}

//ApplyAimRotation() is played every frame in Update()
void ApplyAimRotation()
{
	float aimAngle;
	float aimAnimWeight;
	Vector3 playerPos = transform.position;
	Vector3 playerScreenPos;
	Vector2 playerMouseDistance;
	
	playerScreenPos = Camera.main.WorldToScreenPoint( playerPos );
	playerMouseDistance.x = Input.mousePosition.x - playerScreenPos.x;
	playerMouseDistance.y = Input.mousePosition.y - playerScreenPos.y;
	
	aimAngle = Mathf.Atan( playerMouseDistance.y / playerMouseDistance.x ) * Mathf.Rad2Deg;
	aimAnimWeight = aimAngle / MAX_ROTATION;
		
	if( Input.mousePosition.x >= playerScreenPos.x )
	{
		animAimUp.weight = aimAnimWeight;
		animAimForward.weight = 1.0f - Mathf.Abs( aimAnimWeight );
		animAimDown.weight = -aimAnimWeight;
	}
	if( Input.mousePosition.x < playerScreenPos.x )
	{
		animAimUp.weight = -aimAnimWeight;
		animAimForward.weight = 1.0f - Mathf.Abs( aimAnimWeight );
		animAimDown.weight = aimAnimWeight;
	}
		
	animation.CrossFade( "aim_up", 0.1f );
	animation.CrossFade( "aim_down", 0.1f );
	animation.CrossFade( "aim_forward", 0.1f );
}

There are multiple problems with this approach, but here are the big ones:

  1. The Aim Up and Aim Down animations conflict with each other such that the player cannot aim straight up or straight down. It does not matter if the weight of an animation is 0 or a negative number it will still offset the other animation.
  2. The Aim Forward animation blends with the idle pose in a way that causes the player’s aim to wave left and right rather significantly. This only happens while the aim_forward animation is playing.
  3. If I remove the forward animation, the player cannot aim forward due to the up and down aim animations, which seem to offset the idle pose even if their weight is zero or a negative number.

This has been driving me crazy because I can’t seem to find an example that does this. I’ve seen plenty of places where these animations are made, but cannot find where they are used (for example, the third person shooter demo Unity provides has aim poses, but I can’t seem to find them scripted anywhere and it appears their approach to aiming the player is far more complex). Digital Tutors made a course that included making a character for Unity that included aim poses, but they “skipped” the lesson on making those work.

Please help. I’m completely stumped. I tried switching the blending mode for these animations to Additive, but when I do that they don’t play at all.

If I may…
at the end of ApplyAimRotation, you’re not actually supplying a blend amount. You’re just crossfading over 1/10th of a second.

animation.CrossFade( "aim_up", 0.1f );

Instead, you want to use the Blend function and supply the weight

animation.Blend( "aim_up", animAimUp.weight );