Cinemachine Camera Blend Resetting Tracking Target Rotation

I am having an issue with Cinemachine camera blends resetting the rotations of their Tracking Target.

When I move my mouse, the localRotation of each Tracking Target is rotated by the X value (this is looking up and down with the mouse).

I have StandCamera (Tracking Target: cameraFollow) and CrouchCamera (Tracking Target: crouchFollow).

I can watch CrouchCamera’s camera rotate appropriately when I am standing. When I hit C and go into my Crouch state, I change the camera priorities with XORs like so:

 private void OnPostureChange()
        {
            standingCamera.Priority ^= crouchCamera.Priority; // 1 XOR 0 = 1
            crouchCamera.Priority ^= standingCamera.Priority; // 1 XOR 0 = 1
            standingCamera.Priority ^= crouchCamera.Priority; // 1 XOR 1 = 0
        }

When the CrouchCamera is blended to, it suddenly pops to (0, 0, 0) rotation, but StandCamera keeps it rotation.

Additionally, I have a SprintCamera which has a slightly higher FOV and it is attached to cameraFollow. When I transition to this camera, it also resets the rotation to 0, 0, 0. All three cameras keep their own rotation.

All are enabled and being transitioned between via changing priority.

I have tried Always, Round Robin, and Never Standby Update settings.
I have tried all of the different Blend Hints.
The cameras all have Hard Lock to Target and Rotate With Follow Target.
The virtual cameras and Main Camera have no parent.

No matter what I do, I cannot get them to maintain the rotation of the previous camera or the rotation of their own Tracking Target upon switching.

Thank you for any assistance.

It seems like perhaps it is better for me to just have one virtual camera in this case and lerp the cameraFollow on posture change rather than blend to a new camera.

Although, it would be nice to understand why Cinemachine is doing this behavior.

HardLock/HardRotate with no damping just makes the CM camera directly take its position/rotation from the object it’s tracking. All the state is contained in the tracked object, so whatever code is controlling the rotations of those objects is responsible for the (0,0,0).

That could certainly be it. It doesn’t happen when I’m manually Vector3.Lerp’ing the cameraFollow object, but I think there could be something weird. Here’s the path, let’s see if there’s anything interesting along the way.

Input code:

    public void OnCrouch(InputAction.CallbackContext context)
        {
            if (context.performed)
                CrouchEvent?.Invoke();
        }
    }

In the State Machine:

        public override void Enter()
        {
            Input.CrouchEvent += OnCrouch;
        }
        private void OnCrouch()
        {
            StateMachine.ChangeState("Crouch");
        }

CrouchState code:

        public override void Enter()
        {
            Input.CrouchEvent += OnCrouch;
            _crouchAction.Activate(_controller, _cameraMan);
        }

        public override void Update(float deltaTime)
        {
            Move(_movementSpeed * _crouchMult, deltaTime);
            MouseLook();
        }

        public override void Exit()
        {
            Input.CrouchEvent -= OnCrouch;
            _crouchAction.Activate(_controller, _cameraMan);
        }

        private void OnCrouch()
        {
            // we are currently busy, do nothing
            if (_crouchAction.active)
            {
                return;
            }
            
            StateMachine.ChangeState("Default");
        }
    }

CrouchAction code:

        public void Activate(
            CharacterController controller,
            CameraMan cameraMan
        )
        {
            this.controller = controller;

            active = true;
           
            float targetHeight = isCrouched ? standHeight : crouchHeight;
            Vector3 targetCenter = isCrouched ? standCenter : crouchCenter;
            controller.height = targetHeight;
            controller.center = targetCenter;
            Handler.ChangePosture();
            
            Resolve();
        }

        private void Resolve()
        {
            isCrouched = !isCrouched;
            active = false;
        }

ActionHandler ChangePosture code:

        public void ChangePosture()
        {
            OnPostureChange?.Invoke();
        }

OnPostureChange does the XORs.

Lastly, here is the MouseLook that every state uses:

        protected void MouseLook()
        {
            _lookX -= Input.LookValue.y * Settings.VerticalLookSensitivity;
            _lookX = Mathf.Clamp(_lookX, -Settings.UpperLookLimit, Settings.LowerLookLimit);
            
            StateMachine.cameraMan.cameraFollow.localRotation = Quaternion.Euler(_lookX, 0, 0);
            StateMachine.cameraMan.crouchFollow.localRotation = StateMachine.cameraMan.cameraFollow.localRotation;
            // StateMachine.cameraMan.crouchFollow.localRotation = Quaternion.Euler(_lookX, 0, 0);
            
            StateMachine.transform.rotation *= Quaternion.Euler(0, Input.LookValue.x * Settings.HorizontalLookSensitivity, 0);
        }

It does seem like when I hit C something is happening weird. Here’s a screenshot of Debug.Log values when crouching.

Because the CM cameras are just blindly taking their transforms from the objects they are tracking, the problem will lie with the way those objects are being moved. Cinemachine won’t help you with that.

To debug, try adding shapes to the objects that the cameras are tracking (specifically, the ones that control the rotations). Then, you will see on the screen how they are moving.

EDIT: I see why! It seems obvious in retrospect. When I stop moving my mouse and change positions, my Vector2 from the Mouse is 0,0 because my mouse isn’t moving… and then since I’m setting my LookX by that, that’s why it is snapping to 0. This is also why when I leave it as the same cameraFollow object it works better because it just causes it to not move.

As part of my past debugging I watched the transforms and they rotate appropriately but they snap to 0,0,0 whenever I change states in my state machine. I had a couple of theories that perhaps it is because my call to MoveLook technically refreshes/resets or perhaps because _lookX is private to PlayerBaseState (which all States share). Otherwise it doesn’t make sense for the value of Vector2 from the Mouse input to just shift to 0,0,0.

I should also clarify that they save their angle per state. For example, when I enter Crouch crouchFollow will snap to 0,0,0, but in my Default state it will have the appropriate angle that I was at before crouching.

With my debug logging, I have narrowed it down to see what I’m actually getting from the Input system switches to 0 for some reason.

I have resolved this issue. Thanks for pointing me in the right direction!