Camera shakes while moving around a object

I am trying to make an first person camera for my game. I am using Unity Version 2020.2. For some reason, when I move around objects, my Camera shakes. Here is my camera controller code:

Transform player;
Transform playerHead;

float sensitivity = 300;
float yRotation;
float xRotation;

void Start()
{
    Cursor.lockState = CursorLockMode.Locked;
    player = GameObject.FindGameObjectWithTag("Player").transform;
    playerHead = GameObject.Find("PlayerHead").transform;
}

// Update is called once per frame
void Update()
{
    transform.position = playerHead.position;
    xRotation -= Input.GetAxisRaw("Mouse Y") * sensitivity * Time.deltaTime;
    yRotation += Input.GetAxisRaw("Mouse X") * sensitivity * Time.deltaTime;

    xRotation = Mathf.Clamp(xRotation, -90, 70);

    player.localRotation = Quaternion.Euler(0, yRotation, 0);
    transform.localRotation = Quaternion.Euler(xRotation, yRotation, 0);
}

I’ve tried this code and I didn’t find any problems with shake. However, I’m going to suggest that you radically rethink how you do this.

The usual way of having a 1st person shooter is to make the camera a child of the player. That way, it will follow the player movement/rotation without any intervention from a script. There’s nothing to stop you using a script but you are introducing code that you don’t need.

If you want to stick with your design, it seems wrong to me that the camera should rotate the player (line 24). It’s not the responsibility of the camera to do that and we try and make sure that GameObjects look after their own functions. It’s fine for separate Game Objects to handle input simultaneously. So Player can check for Mouse X movement to rotate the body and the Camera can check for Mouse Y to look up and down.

When the camera is a child, rotating the body left/right also rotates the camera so nothing to do there. When you read MouseY, that rotates the camera only on the X axis (it’s a bit like moving the head up and down - the body stays still otherwise).

Although we use Quaternions for setting transform.rotation, if you are doing a simple rotation, you can do that using transform.Rotate. However, you do not need Time.DeltaTime in these calculations. The amount of movement you get from Input.GetAxisRaw is the amount that has happened in this frame so you need to use the full amount. We say that Input.GetAxisRaw is framerate independent.

If you make the camera a child of the player and handle Player rotation in that script, then your camera becomes much simpler - something like this:

using UnityEngine;

public class CameraScript : MonoBehaviour
{
    [SerializeField] float sensitivity = 50;

    void Start()
    {
        Cursor.lockState = CursorLockMode.Locked;
    }

    void Update()
    {
        float rotationUpDown = Input.GetAxisRaw("Mouse Y") * sensitivity;
        transform.Rotate(rotationUpDown, 0, 0);
    }
}

The only thing you might want to add is something that limits you rotating too far up and down. I’ll leave you to have a think about that but come back if you can’t figure it out or find it elsewhere.

OK - a fairly length reply…

There are some good things in your script. Well done, for example, in identifying that you need to convert local movement to world movement for RB.velocity. Also for preserving the rb.velocity.y, which is something that a lot of people forget. However, the code can be improved in a couple. of ways.

Firstly, you are using Input.GetAxis. If using the keyboard, this is not a simple on and off, as many players might expect. The value eases up from 0 to 1 then, when you realise, eases back down to 0 again. If you want you character to have full speed immediately and stop as soon as you take your finger off the keyboard, you’ll need to use Input.GetAxisRaw. You can tell this easing is happening at the moment because, when you release your finger, the character keeps moving for a while until it has slowed down to zero.

Secondly, you have forgotten normalise the input. Normalisation makes sure that diagonal movement is not faster than orthogonal.

Thirdly, there is a better way of translating local to world - use transform.TransformDirection. Were I writing the code, I’d do something like this:

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float speed = 12f;
    Rigidbody rb;

    private void Start()
    {
        rb = GetComponent<Rigidbody>();
    }

    void Update()
    {
        float x = Input.GetAxis("Horizontal");
        float z = Input.GetAxis("Vertical");

        Vector3 move = new Vector3(x, 0, z).normalized * speed;
        move = transform.TransformDirection(move);
        move.y = rb.velocity.y;

        rb.velocity = (move);

    }
}

Remember that rb.velocity is in world space anyway so you didn’t need to multiply velocity.y by Vector3.up.

As for shake, I don’t see any, even on the video. It seems a little jumpy but that could be the frame rate being dropped by Unity or you video capture software.

Finally - for now - best not to post code onPastebin. Posting here gives you syntax colouring, doesn’t have adverts and keeps everything in one place. I’ve seen people post code on 3rd party sites only to delete them later, leaving these answers without key information.