Search code examples
c#unity-game-enginecameragame-engine

Third-person camera stutters when I walk backwards


I have a third-person code, but the camera stutters when I walk backwards! Does anyone have a solution? The camera is separate from the player, the player rotates according to the view of the camera.

PlayerController.cs

if (Input.GetKey (KeyCode.LeftShift)) {
    if(Input.GetKey(KeyCode.W) || Input.GetAxis ("Vertical") > 0) moveDirection += camera.forward;
    if(Input.GetKey(KeyCode.S) || Input.GetAxis ("Vertical") < 0) moveDirection += -camera.forward;
    if(Input.GetKey(KeyCode.A) || Input.GetAxis ("Horizontal") < 0) moveDirection += -camera.right;
    if(Input.GetKey(KeyCode.D) || Input.GetAxis ("Horizontal") > 0) moveDirection += camera.right;

    if (Input.GetAxis ("Horizontal") != 0 || Input.GetAxis ("Vertical") != 0) {
        //Multiply it by speed.
        moveDirection.Normalize ();
        moveDirection *= run;
        //Applying gravity to the controller
        moveDirection.y -= gravity * Time.deltaTime;
        //Making the character move
        controller.Move (moveDirection * Time.deltaTime);
    }
    moveDirection.y = 0f;
    if (moveDirection != Vector3.zero) {
        transform.rotation = Quaternion.RotateTowards (transform.rotation, Quaternion.LookRotation (moveDirection), rotationSpeed * Time.deltaTime);
    }
}

Camera.cs

// Update is called once per frame
void Update () {

    // We setup the rotation of the sticks here
    float inputX = Input.GetAxis ("RightStickHorizontal");
    float inputZ = Input.GetAxis ("RightStickVertical");
    mouseX = Input.GetAxis ("Mouse X");
    mouseY = Input.GetAxis ("Mouse Y");
    finalInputX = inputX + mouseX;
    finalInputZ = inputZ - mouseY;

    rotY += finalInputX * inputSensitivity * Time.deltaTime;
    rotX += finalInputZ * inputSensitivity * Time.deltaTime;

    rotX = Mathf.Clamp (rotX, -clampAngle, clampAngle);

    Quaternion localRotation = Quaternion.Euler (rotX, rotY, 0.0f);
    transform.rotation = localRotation;


void LateUpdate () {
    CameraUpdater ();
}

void CameraUpdater() {
    Transform target = CameraFollowObj.transform;
    // set the target object to follow

    //move towards the game object that is the target
    float step = CameraMoveSpeed * Time.fixedDeltaTime;
    transform.position = Vector3.MoveTowards (transform.position, CameraFollowObj.transform.position, step);
}

Solution

  • In my experience when you have a smooth camera that stutters, it's because the camera - once it has gotten up to speed - moves faster than the object it is following:

    Problem

    1. Object moves
    2. Camera starts to slowly move towards object
    3. It catches up and stops
    4. Camera to slowly start move towards object
    5. Repeat 2-4; stuttery behaviour

    Solution

    Simple; tweak the camera speed variables to make sure it never completely catches up when the object is moving, but rather stays just behind so it doesn't have to stop every few frames.



    Bonus reading material

    Other sources will often explain their solution is to incorrectly put the code in FixedUpdate to miraculously get rid off the stuttery behaviour. Sometimes getting the correct behaviour from doing this is due to a series of things:

    • Update runs every frame update, so the time step (Time.deltaTime) here depends on machine but on my machine it will be, on average, every 0.007 seconds.
    • FixedUpdate runs on a fixed timestep, standard is 0.002 but can be changed in settings.
    • The code: float step = CameraMoveSpeed * Time.deltaTime; Let's assume that CameraMoveSpeed is 1, for the sake of simple maths.

    So with these numbers we can understand that putting the code in Update gives the following result

    Update

    Every 0.007 seconds, we update the camera lerp tick with CameraMoveSpeed * 0.007

    FixedUpdate

    Every 0.02 seconds, we update the camera lerp tick with CameraMoveSpeed * 0.007

    Result

    They move the same length every tick, but the ticks are less frequent in the FixedUpdate, which results in a slower camera smoothing and thus, "solves" the issue described as answer to this post of, in a way, tweaking camera speed variables to get a slower camera follow but instead of tweaking variables you havev moved your code to the physics update instead of the frame update and you only solved the issue by accident, without understanding the cause.