Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Character Rotation Camera Jitter

Discussion in 'Scripting' started by Dorscherl, Jul 15, 2021.

  1. Dorscherl

    Dorscherl

    Joined:
    May 29, 2017
    Posts:
    26
    As the title suggest, I'm experiencing some camera jitter when I attempt to rotate the character. I've run multiple instances and even a build to play around with the camera and can't get it too smooth out and stop jittering.

    I'm attempting to make a third and first person perspective on my character and opted to use a transform dubbed 'Head Controller' to handle head IK and more importantly keep the third and first person camera's synced up when switching between the two.

    The character hierarchy looks a little like this:
    • Character (root)
      • Head Controller
        • Look Target
      • Mesh (Animator)
        • Skeleton
      • Cameras
        • First Person
        • Third Person
    The character has a Rigidbody component with freeze rotation set to true and interpolation is enabled.
    The velocity gets set directly and the movement very smooth and responsive so I know my camera jitter is not because of movement but strictly by when I rotate the character.

    Haven't implement the third person camera yet but the first person camera simply takes a follow transform (eyes) and the head controller transform to position itself to the eyes and copy the rotation of the head controller. This gets updated with the head controller.

    This link will take you to video demonstrating the issues. You'll see that simply moving without moving the mouse that everything is smooth and fine but when I rotate the mouse and character the objects jitter.



    Here is my code:
    Note:
    I reset the head controller and first person camera in PerformRotation() to counter the local rotation caused when rotating the root transform. This works well as it eliminates jerky behaviour when it corrects it's rotation next frame also fixes the camera from clipping/jittering into the mesh when it rotates.

    Code (CSharp):
    1.  
    2.         //
    3.         protected virtual void PerformRotation()
    4.         {
    5.             if (rotationMode == RotationMode.None) return;
    6.  
    7.             var rotation = HeadController.rotation;
    8.  
    9.             if (_perspectiveMode == PerspectiveMode.FirstPerson)
    10.             {
    11.                 Vector3 viewDirection = HeadController.forward;
    12.                 if (characterInput.MoveInput.magnitude == 0)
    13.                     ViewRotation(viewDirection, true);
    14.                 else
    15.                     RotateTowards(viewDirection, true);
    16.             }
    17.             else
    18.             {
    19.                 switch (rotationMode)
    20.                 {
    21.                     case RotationMode.OrientToMovement:
    22.                         // Orient towards input move direction vector (in Character's plane)
    23.                         var movementDirection = moveDirection;
    24.                         RotateTowards(movementDirection);
    25.                         break;
    26.                     case RotationMode.OrientToCameraViewDirection:
    27.                         if (_headController == null) return;
    28.                         Vector3 viewDirection = HeadController.forward;
    29.                         if (characterInput.MoveInput.magnitude == 0)
    30.                             ViewRotation(viewDirection, true);
    31.                         else
    32.                             RotateTowards(viewDirection, true);
    33.                         break;
    34.                     case RotationMode.OrientWithRootMotion:
    35.                         if (rootMotionController)
    36.                             ApplyRootMotionRotation();
    37.                         break;
    38.                 }
    39.             }
    40.             HeadController.rotation = rotation;
    41.             //fpCamera.DoUpdate(); //reset camera rotation to match
    42.         }
    43.  
    44.         protected virtual void UpdateHeadController()
    45.         {
    46.             Vector3 up = UpVector;
    47.  
    48.             _headRotation = Quaternion.AngleAxis(_lookInput.x, Vector3.up) * Quaternion.AngleAxis(_lookInput.y, Vector3.right);
    49.             _headR = Quaternion.FromToRotation(_lastUp, up) * _headR;
    50.             _headRotation = _headR * _headRotation;
    51.             _lastUp = up;
    52.  
    53.             HeadController.rotation = Quaternion.Slerp(HeadController.rotation, _headRotation, 30 * Time.deltaTime);
    54.             fpCamera.DoUpdate();
    55.         }
    56.  
    Actual Rotation Code
    Code (CSharp):
    1.  
    2.         // allows the head to rotate freely of the character when stationary
    3.         protected virtual void ViewRotation(Vector3 worldDirection, bool isPlanar = true)
    4.         {
    5.             if (moveDirection != Vector3.zero || _maxHeadTurnAngle <= 0)
    6.             {
    7.                 RotateTowards(worldDirection, isPlanar);
    8.             }
    9.             else
    10.             {
    11.                 // Get camera forward in the character's rotation space
    12.                 Vector3 camRelative = transform.InverseTransformDirection(worldDirection);
    13.  
    14.                 // Get the angle of the camera forward relative to the character forward
    15.                 float angle = VectorMath.GetYaw(camRelative);
    16.  
    17.                 // Making sure the angle does not exceed maxangle
    18.                 if (Mathf.Abs(angle) > Mathf.Abs(_maxHeadTurnAngle))
    19.                 {
    20.                     float a = angle - _maxHeadTurnAngle;
    21.                     if (angle < 0f) a = angle + _maxHeadTurnAngle;
    22.                     var targetRotation = Quaternion.AngleAxis(a, UpVector) * characterMovement.rotation;
    23.                     transform.rotation = targetRotation;
    24.                     //RotateTowards(targetRotation);
    25.                 }
    26.             }
    27.         }
    28.  
    29.         protected virtual void RotateTowards(Vector3 worldDirection, bool isPlanar = true)
    30.         {
    31.             Vector3 characterUp = UpVector;
    32.  
    33.             if (isPlanar)
    34.                 worldDirection = worldDirection.projectedOnPlane(characterUp);
    35.  
    36.             if (worldDirection.isZero())
    37.                 return;
    38.  
    39.             Quaternion targetRotation = Quaternion.LookRotation(worldDirection, characterUp);
    40.             RotateTowards(targetRotation);
    41.         }
    42.  
    43.         protected virtual void RotateTowards(Quaternion targetRotation)
    44.         {
    45.             transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationRate * Mathf.Deg2Rad * Time.deltaTime); ;
    46.         }
    Rotation gets executed in LateUpdate() like so:
    Code (CSharp):
    1.         void LateUpdate()
    2.         {
    3.             abilitySystem.AbilityLateUpdate(Time.deltaTime);
    4.             Debug.Log(currentState);
    5.  
    6.             UpdateHeadController();
    7.            
    8.             PerformRotation();
    9.  
    10.             //fpCamera.DoUpdate();
    11.         }
     
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    How do you move your character? What are the settings on the rigidbody?

    I would also be concerned with using a Slerp for your rotation that's using moving targets and a variable T parameter, as that's going to create incredibly inconsistent and frame-rate dependent time sliced updates.
     
  3. Dorscherl

    Dorscherl

    Joined:
    May 29, 2017
    Posts:
    26
    Freeze rotation is true and interpolation is set to interpolate. Velocity is set every fixed updated and it is smooth. I attached a static camera to check it and didn't get any jittery results until tried enabling rotation in the script.

    I believe I am real careful with slerp and since the rotation is calculated in world space and fixed, I figure it's easy to just copy the head controllers rotation and reapply it after the character's root has been rotated to counter it.

    I need some form of smoothing or else it'll be too snappy. Can't have the character do a complete 180 in 0.1ms flat lol

    I'll see if changing out slerp will change things
     
  4. Dorscherl

    Dorscherl

    Joined:
    May 29, 2017
    Posts:
    26
    Still have the problem with setting the rotation directly without Slerp/Lerp
     
  5. Dorscherl

    Dorscherl

    Joined:
    May 29, 2017
    Posts:
    26
    So for anyone wondering, I fixed *part* of the problem by putting character rotation in a coroutine that executes after fixed update. I also separated the head controller into a brother-brother relationship under a empty holder parent just to prevent inconsistencies. Personally not ideal but it works.

    Here's where I discovered the fix:
    https://forum.unity.com/threads/rotating-a-rigidbody-fps-player-makes-objects-jitter.1065686/

    Problem now is the mesh of the character looks like it stutters when rotating to match the camera. Which probably wont be helped because the rigidbody being updated every 'late' fixed update. Going to try and eliminate that best I can by trying to reset camera rotation/positioning after character rotation.
    Other suggestions would be appreciated.
     
    Zellator likes this.
  6. MarekNijaki

    MarekNijaki

    Joined:
    Jan 28, 2018
    Posts:
    8
    I had similar issues with jittering that I managed to resolve and wanted to share (hope it helps someone else).

    I have a custom third person character controller that is physics based.
    I use Cinemachine + 'CinemachineInputProvider' for controlling cameras.
    When rotating camera, character is rotating towards the camera forward direction.
    I use the new Input System.

    Explanation of what you should be mindful of to eliminate jittering/stutter (if you have physics based controller):
    * You should read inputs in 'Update()'
    * You should use 'InputValueGain' in Cinemachine camera aim section, to make Input framerate independent.
    https://forum.unity.com/threads/cinemachine-and-framerate.529992/
    * You should set appropiate values of acceleration and decceleration on Cinemachine camera aim section (it doesn't fix jitter per se, but it makes overall feeling more smoothed)
    * You should apply movement by using 'Rigidbody' methods eg. 'rigidbody.Move()', 'rigidbody.AddForce()' and etc
    * You should NOT manipulate transform position directly, to move object
    * You should apply movement in 'FixedUpdate()'
    * You should set 'Interpolation' mode on 'Rigidbody' to 'Interpolate'
    * Because rotation of the player is depending on camera forward direction you need to apply player rotation AFTER camera has rotated. Camera is rotating on 'Update' calls, because of that you can not apply player rotation in
    'FixedUpdate()' - 'FixedUpdate()' in Unity is called before 'Update()'. So you need to create a delayed/late 'FixedUpdate()' and apply player rotation there (example provided below).
    * When rotating the player it is often that before the player fully rotates towards the camera forward direction, the camera will change to face a new direction.
    In that case the player will start rotating to the newer forward direction of the camera (before the player finished his first rotation).
    This could lead to some other type of jittering, that can be smoothed by using 'SmoothDampAngle()' or 'SmoothDamp()'.
    https://docs.unity3d.com/ScriptReference/Mathf.SmoothDampAngle.html
    https://docs.unity3d.com/ScriptReference/Vector3.SmoothDamp.html
    * My full project is publicly available and can be accessed via
    https://github.com/mnijaki/AutoAimBowAndArrow


    Code (CSharp):
    1. private readonly WaitForFixedUpdate _waitForFixedUpdate = new();
    2. private float _tmpRotationVelocity;
    3. [SerializeField]
    4. private float _rotationSmoothTime = 0.1F;
    5.  
    6. private void Awake()
    7. {
    8.    StartCoroutine(LateFixedUpdate());
    9. }
    10.  
    11. private IEnumerator LateFixedUpdate()
    12. {
    13.   while(true)
    14.   {
    15.     yield return _waitForFixedUpdate;
    16.     HandleRotation();
    17.    }
    18. }
    19.  
    20. private void HandleRotation()
    21. {
    22.   // Rotate player in direction where camera is facing ('SmoothDampAngle' is used, so no jitter occurs).
    23.   var targetHorizontalAngle = Mathf.SmoothDampAngle(_rigidbody.rotation.eulerAngles.y,
    24.                                                     _cameraTransform.eulerAngles.y,
    25.                                                     ref _tmpRotationVelocity,
    26.                                                     _rotationSmoothTime,
    27.                                                     float.MaxValue,
    28.                                                     Time.fixedDeltaTime);
    29.   Quaternion targetRotation = Quaternion.Euler(0.0F,targetHorizontalAngle,0.0F);
    30.   _rigidbody.MoveRotation(targetRotation);
    31. }
     
    Last edited: Jul 6, 2022
    curryjl and Dorscherl like this.