Very peculiar aiming issue - OnAnimatorMove()

So I’m ripping my hair off atm (what’s left of it)

Basically I have 3 aim animations that I blend together to create smooth straight/up/down aiming. And I have a ‘gunTip’ transform at the tip of the character’s gun. The ‘forward’ of the tip is coming out of the gun’s barrel. This is where I shoot from.

When I fire, I cast a ray from the tip with the tip’s forward being the direction. The animations are handled from script (not root motion). For some reason, if I call the shoot method from OnAnimatorMove, the raycast doesn’t seem to want to follow the tip’s forward when I aim up/down!

I made a 1 showing the problem, my code, and the two different aid-band fixes I made.

  1. The first fix is calling TryShoot
    from the PlayerController’s Update,
    and not OnAnimatorMove, it worked
    pretty well, but of course without
    an animation.
  2. The second fix, just to prove that the animation has nothing to do with the problem, I created a tipFwd Vector3 in Weapon, and assigned it to the gun’s tip in Update, and used that variable when I do raycast.

The first fix actually produces more accurate results than the second. Notice in my OnDrawGizmos I draw a red sphere (which I get its position from the shooting hit point) and a yellow sphere which I draw from the hit point I get from raycasting in OnDrawGizmos. What drives me crazy is that I’m using the “SAME” raycast both when I shoot and OnDrawGizmos, yet the red sphere doesn’t appear where it should, while the yellow one is placed appropriately…

Here’s my PlayerController’s Update and OnAnimatorMove:

	private void Update()
	{
		if (animator)
		{
			animator.speed = animationSpeed;
			animator.SetFloat("Speed", Input.GetAxis("Vertical"));
			animator.SetBool("IsAiming", Input.GetMouseButton(1));
			animator.SetBool("IsRunning", Input.GetKey(KeyCode.LeftShift));
		}
		transform.Rotate(0f, Input.GetAxis("Horizontal") * Time.deltaTime * rotationSpeed * 100f, 0f);
	}

	private void OnAnimatorMove()
	{
		var baseLayer = animator.GetCurrentAnimatorStateInfo(0);
		var secondLayer = animator.GetCurrentAnimatorStateInfo(1);
		Func<int, bool> baseIsInState = hash => baseLayer.nameHash == hash;
		Func<int, bool> secondLayerIsInState = hash => secondLayer.nameHash == hash;

		bool isAiming = Input.GetMouseButton(1) && secondLayerIsInState(aimState);

		if (isAiming)
		{
			if (!resetAim)
			{
				resetAim = true;
				animator.SetFloat("AimY", 0f);
			}
			animator.SetFloat("AimY", Mathf.Clamp(animator.GetFloat("AimY") + Input.GetAxis("Mouse Y"), -1f, 1f));
			animator.SetBool("IsShooting", Input.GetMouseButton(0) && weapon.TryShoot());
		}
		else
		{
			resetAim = false;
		}

		// Movement...
	}

And here’s my Weapon’s Shoot/TryShoot:

	public bool TryShoot()
	{
		if (shooting conditions are not met)
		{
			return false;
		}

		Shoot();
		return true;
	}

	private void Shoot()
	{
		// Sound, muzzle and ammo decreasing...

		RaycastHit hit;
		if (Physics.Raycast(gunTip.position, gunTip.forward, out hit, Distance, shootingMask))
		{
			dbgLastShot = hit.point; // this is how I determine the red sphere's position
			// irrelevant code...
		}
	}

The problem doesn’t make any sense to me… But I have a good amount of certainty that it’s a gotcha related to Animator/Mecanim and/or OnAnimatorMove… If you have any idea I’d really appreciate it!

Thanks a lot!

I refer you back to the InPlace page. Notice how simple they have kept it, modifying transforms only. It is perfectly fine to Get parameters from the Animator but Setting them here is not a good idea, as far as I know.

In OAM() you can directly modify the transform of a bone. For example rotating the spine, arms etc to re-position them as desired. I did this same thing to make custom IK in Free Edition, so I could swing a sword and always cut through a particular point in space (or target).