Camera Jitter on Rotation (not movement)

Viewed 145

My camera jitters on rotation, but not movement. This script is the only asset in a new project, and it's attached to the camera - it's supposed to be a free-flying camera. I added a few cubes into the environment so I could check it was moving correctly, and it stutters several times a second whenever it's rotated. Movement works just fine.

I've tried changing camera clipping planes, I've tried moving either movement or rotation or both into Update() instead, removing the default camera and adding my own, and since it's an otherwise empty project I'm showing over 500fps so don't have any performance issues - nothing has had any effect, for better or worse.

Any suggestions welcome! If someone has a minute to open up a default project and slap this script on the camera and make sure it's not something on my end, that would at least rule that out and I'd really appreciate it. Thanks in advance.

public class CameraController : MonoBehaviour
{
    public Vector2 mouseSensitivity = Vector2.one;
    public Vector3 moveSensitivity = Vector2.one;
    public Transform camTransform;
    Vector3 camEulers = Vector3.zero;

    void Start()
    {
        camTransform = FindObjectOfType<Camera>().transform;
    }

    void LateUpdate()
    {
        KeyboardMovement();
        LockedMouseRotation();
    }

    void KeyboardMovement()
    {
        camTransform.position += Input.GetAxis( "Vertical" ) * camTransform.forward * Time.deltaTime * moveSensitivity.y;
        camTransform.position += Input.GetAxis( "Horizontal" ) * camTransform.right * Time.deltaTime * moveSensitivity.x;
    }

    void LockedMouseRotation()
    {
        camEulers.x -= Input.GetAxis( "Mouse Y" ) * mouseSensitivity.y * Time.deltaTime;
        camEulers.y += Input.GetAxis( "Mouse X" ) * mouseSensitivity.x * Time.deltaTime;
        camEulers.x = Mathf.Clamp( camEulers.x, -90.0f, 90.0f );

        camTransform.rotation = Quaternion.Euler( camEulers );
    }
}
1 Answers

Don't use Time.deltaTime here.

Your goal here is to convert measurements of linear distance (GetAxis("Mouse X") and GetAxis("Mouse Y")) into a measurement of angular distance (the angular distance you are using to adjust the camera's Euler angles). For that you only need a constant factor, which can be your mouseSensitivity components.

But GetAxis gets multiplied by Time.deltaTime in other places and works perfectly fine. Why do those work like that?

Your goal is not converting to distance from "throttle position" like one might from GetAxis("Horizontal") or GetAxis("Vertical") or GetAxis("customKeyButtonOrThrottleAxis") . That is a situation where you would want to multiply by a time (Time.deltaTime) to calculate a distance if you are using the position of the button or throttle as velocity. This is because distance = speed * time.

In some cases you might multiply by Time.deltaTime to calculate a velocity or speed if the button or throttle is to represent acceleration, because speed = acceleration * time.

Okay, so why doesn't Time.deltaTime work here?

When you use GetAxis("Mouse X") or GetAxis("Mouse Y"), you get a measurement of pixel difference in position since the last frame.

The typical way a mouse driven camera works in games is that moving the mouse the same distance in the same direction should move the camera the same way (assuming you don't move the camera all they way up or down of course).

So what that means is that multiplying by Time.deltaTime does here is often actually the opposite of what you might expect - moving the mouse the same distance moves the camera's rotation differently depending on how many frames that movement occurred over and how quickly those frames were rendered.

The faster the frames were rendered (that means, a smaller Time.deltaTime), the less camera rotation the same mouse distance becomes. This results in unpredictable jitter especially when the very act of moving the camera can drastically change the framerate.

So how do I fix it then?

So, the only thing you need to do here is to remove the multiplication by Time.deltaTime:

camEulers.x -= Input.GetAxis( "Mouse Y" ) * mouseSensitivity.y;
camEulers.y += Input.GetAxis( "Mouse X" ) * mouseSensitivity.x;

A similar situation where you might want to include Time.deltaTime is if you want to calculate the speed or velocity of the mouse movement.

But, in that case, you would want to divide by Time.deltaTime. This is because speed = distance / time.

Vector2 mouseVelocity = Vector2.Scale(mouseSensitivity,
                                      new Vector2(Input.GetAxis("Mouse X"),
                                                  Input.GetAxis("Mouse Y"))
                                     ) / Time.deltaTime;

In most cases, you probably don't want to be concerned with the speed/velocity of the mouse movement. But, for example, those interested in a super monkey ball type game using a track ball input might find this side bit useful!

Related