Based on the Unity docs, the execution order per frame is FixedUpdate => Update => LateUpdate
In the small test script below I change the rotation of the player’s transform to whatever the current value of cameraYRot is specified to. This maybe gets called several times, but there is no collider, smoothing or physics at all and so the rotation should be exactly that value after FixedUpdate.
Then Update gets called by the engine and here I set the cameraRotationPivot to the value cameraYRot. So both GameObjects should have now the same rotation => the player and the camera should look into the same direction.
In LateUpdate I’m reading the new cameraYRot from the user’s input. This doesn’t get used this frame anymore. And everything gets rendered.
I would expect that the player and the Camera have the same rotation and no jitter happens, but I get a jittery rotation and appreciate any help to understand this problem.
The script below has several modes for reading the input (Update, LateUpdate) and also several modes for updating the camera rotation to play around with.
Info: You can drop this script in an empty scene to any existing GameObject and it will create all the needed stuff + you can play around with the 2 setting modes to check the results.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test_RotJitter_2 : MonoBehaviour
{
// some update modes to play around and test different settings
public CamUpdateModes CameraUpdateMode = CamUpdateModes.Update_BeforeRead;
public InputUpdateModes InputUpdateMode = InputUpdateModes.Update;
// the desired y-rotation
public float cameraYRot;
public float rotationSpeed = 1000;
// a character or cube, where you can see jitter effects (capsule is rather bad)
public Transform player;
// the Pivot is an empty GameObject placed at exactly the same position as the player
// it contains the MainCamera, put at any angle you want to look at the player
public Transform cameraRotationPivot;
// the input from the user
public float xAxisInput;
public enum InputUpdateModes
{
Update, LateUpdate
}
public enum CamUpdateModes
{
Update_BeforeRead, Update_AfterRead,
FixedUpdate_BeforeMove, FixedUpdate_AfterMove,
LateUpdate_BeforeRead, LateUpdate_AfterRead
}
private void Reset()
{
GameObject pivot = new GameObject();
cameraRotationPivot = pivot.transform;
cameraRotationPivot.name = "Camera Rotation Pivot";
GameObject goPlayer = GameObject.CreatePrimitive(PrimitiveType.Cube);
player = goPlayer.transform;
player.name = "PLayer";
player.position = new Vector3(0, 3, 0);
cameraRotationPivot.position = player.position;
Transform camTransform = Camera.main.transform;
camTransform.position = player.position - player.forward * 5 + Vector3.up;
camTransform.rotation = Quaternion.Euler(15, 0, 0);
camTransform.parent = cameraRotationPivot;
}
void ReadInput()
{
xAxisInput = Input.GetAxis("Mouse X");
cameraYRot = cameraYRot + xAxisInput * rotationSpeed * Time.deltaTime;
}
void RotateCamera()
{
cameraRotationPivot.rotation = Quaternion.Euler(0, cameraYRot, 0);
}
// we always rotate the player in FixedUpdate,
// because later the physics needs to be done here
// we never read input in FixedUpdate, because it can be called several times
private void FixedUpdate()
{
if (CameraUpdateMode == CamUpdateModes.FixedUpdate_BeforeMove) RotateCamera();
player.rotation = Quaternion.Euler(0, cameraYRot, 0);
if (CameraUpdateMode == CamUpdateModes.FixedUpdate_AfterMove) RotateCamera();
}
// Update is called once per frame
void Update()
{
if (CameraUpdateMode == CamUpdateModes.Update_BeforeRead) RotateCamera();
if (InputUpdateMode == InputUpdateModes.Update) ReadInput();
if (CameraUpdateMode == CamUpdateModes.Update_AfterRead) RotateCamera();
}
private void LateUpdate()
{
if (CameraUpdateMode == CamUpdateModes.LateUpdate_BeforeRead) RotateCamera();
if (InputUpdateMode == InputUpdateModes.LateUpdate) ReadInput();
if (CameraUpdateMode == CamUpdateModes.LateUpdate_AfterRead) RotateCamera();
}
}