Smooth Motion Without Changing Fixed Timestep

I started learning to use Unity and C# scripting a few weeks ago and I have a hard time achieving smooth movement for my player object in a 3D environment using physics.

I am using 3 game objects (player, camera, focal point) and 2 scripts (player movement and camera movement). My main camera is a child object of a focal point empty object. The camera movement script is attached to the focal point object. My player and focal point objects use rigidbodies with interpolation.

My goal is to have a 3rd person camera following an object that can move freely on the x and z axis ( + space bar for additional impulse), and rotates on the y axis based on focal point/camera rotation.

This is my code for the focal point/camera movement:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveCamera : MonoBehaviour
{
    private GameObject player;
    private Rigidbody camRb;
    private float speed;

    private void Start()
    {
        // Assign references/variable
        player = GameObject.Find("Player");
        camRb = GetComponent<Rigidbody>();
        speed = 200;
    }

    void FixedUpdate()
    {
        RotateCamera();
    }

    private void LateUpdate()
    {
        FollowPlayer();
    }

    void RotateCamera()
    {
        // Get horizontal axis and rotate on input
        float horizontalInput = Input.GetAxis("Horizontal");
        Quaternion targetRot = Quaternion.AngleAxis(horizontalInput * speed * Time.deltaTime, Vector3.up);
        camRb.rotation *= targetRot;
        // or transform.Rotate(Vector3.up, horizontalInput * speed * Time.deltaTime); ?
    }

    void FollowPlayer()
    {
        Vector3 velocity = Vector3.zero;
        transform.position = Vector3.SmoothDamp(transform.position, player.transform.position, ref velocity, 0.02f);
    }
}

And this is my code for the player movement:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    private Rigidbody playerRb;
    private GameObject focalPoint;
    private float speed;
    private float boostStrength;
    private bool boostInput;

    void Start()
    {
        // Assign references/variables
        playerRb = GetComponent<Rigidbody>();
        focalPoint = GameObject.Find("Focal Point");
        speed = 100;
        boostStrength = 100;
    }

    void Update()
    {
        // Check for boost input
        if (Input.GetKeyDown(KeyCode.Space))
        {
            boostInput = true;
        }
    }

    void FixedUpdate()
    {
        ForwardMovement();
    }

    void ForwardMovement()
    {
        // Move towards focal point on vertical input
        float verticalInput = Input.GetAxis("Vertical");
        playerRb.AddForce(focalPoint.transform.forward * verticalInput * speed);

        // Temporary boost on space bar
        if (boostInput)
        {
            playerRb.AddForce(focalPoint.transform.forward * verticalInput * boostStrength, ForceMode.Impulse);
            boostInput = false;
        }

        // Smooth rotation based on camera rotation and horizontal input - THIS IS WHERE THINGS DON'T GO AS EXPECTED
        float horizontalInput = Input.GetAxis("Horizontal");
        float velocity = 0.0f;
        float targetRotation = Mathf.Atan2(horizontalInput / 3, verticalInput) * Mathf.Rad2Deg + focalPoint.transform.eulerAngles.y;
        targetRotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref velocity, 0.02f);
        Quaternion targetRot = Quaternion.AngleAxis(targetRotation, Vector3.up);
        playerRb.MoveRotation(targetRot);
    }

For simplicity’s sake, let’s assume that last part was something like this instead:

// Rotation based on camera rotation
        float cameraY = focalPoint.transform.rotation.eulerAngles.y;
        transform.localRotation = Quaternion.Euler(0, cameraY, 0);
}

These last two lines of codes are what I was using when I noticed that although the player’s forward motion was smooth, the rotation motion (which is based on focal point/camera rotation) stuttered.

I tried to find solutions to this issue by reading the scripting API and checking out forum threads, and learned a lot. I tried several bits of code that I thought would help smooth out movement, including rigidbody.MoveRotation, Slerp, SmoothDamp, RotateTowards, FromToRotation, LookRotation, etc.
These gave varied and interesting results, but everything I tried had the same stuttering effect to some extent.

I then read about execution order, timesteps and the differences between Update(), FixedUpdate() and LateUpdate(). I tried to apply what I learned in my scripts by using Update() to detect user input, LateUpdate() for the FollowPlayer() camera function, and FixedUpdate() for the actual movement.

However, this didn’t solve my problem. To be clearer, everything runs smoothly, except the rotation of the player game object.

Now, I changed the fixed timestep from 0.02 to 0.01, and all motion is smooth (enough). However, I read that changing the fixed timestep was not usually recommended, as it could affect performance.

So, here comes my actual question: are there ways to achieve smooth motion without changing the fixed timestep? how do more experienced coders deal with such issues?

I’m realising now that this post is rather long, so thank you so much for taking the time to read it up to this point!
(Also, this is my first time posting and english is not my mother tongue, so don’t hesitate to mention incorrect format/language)

Is the rotation of the player unsmooth? Or just the rotation of the camera following?

If it is only the camera you’re having an issue with, consider installing Cinemachine and letting that take care of it.

Cinemachine is a package from Unity and it has a lot of features.

You are rotating the camera based on user input and setting the player rotation to match rotation of the camera, which is a rather unusual way to implement player rotation. Is your camera movement smooth (as opposed to the player movement)? If so, then if you made horizontal input rotate the player instead of the camera, player rotation would be smooth. Then you can control your camera however you want.

Thank you for your reply!
The camera movement is smooth, only the player object jitters when rotating.
Transferring the horizontal input and rotation to the player movement script seems to only reverse the problem (the player is smoother, but the camera and the rendered scene jitter as the camera follows the player).
However, I tried your solution in another empty scene with new objects and scripts, and it seemed to work just fine, so I’m guessing it might actually have another problem somewhere, like an optimization issue or something like that.

Thank you, I will check it out !

I guess my main point was that if you let input control the player instead of the camera, you can control the camera however you want, so you can manually go in and add smoothing for example (which may be inappropriate to put on the player), or even have a fixed camera, or a top down view where the camera moves but doesn’t rotate, anything you want really. I guess it’s more of a design problem than a technical problem.

Remember, the camera simply captures the game world. The player object has no business knowing where the camera is, or even the fact that it exists.