Camera third person rotate around gets rolled when player moves on a slope

Hey so I’m making this car game where the camera is supposed to be able to be moved around the player by holding right click. It sort of works as it is, but when the player moves on a sloped surface, the camera’s roll doesn’t follow the player properly. Does anyone know how I can fix this in code?
This is what I have in my update function:

// Rotation
        if (Input.GetMouseButton(1)) // Right-click is pressed
        {
            float horizontalInput = Input.GetAxis("Mouse X") * rotationSpeed;
            float verticalInput = Input.GetAxis("Mouse Y") * rotationSpeed;

            // Rotate the camera around the target
            transform.RotateAround(target.position, Vector3.up, horizontalInput);
            transform.RotateAround(target.position, transform.right, -verticalInput);
        }

Here is what it looks like when I drive my car in a semicircle on a sloped surface once.

as you can see, the camera ends up being rolled a bit when it goes back on flat ground.

Camera stuff is pretty tricky… you may wish to consider using Cinemachine from the Unity Package Manager.

There’s even a dedicated forum: Unity Engine - Unity Discussions

If you insist on making your own camera controller, do not fiddle with camera rotation.

The simplest way to do it is to think in terms of two Vector3 points in space: where the camera is LOCATED and where the camera is LOOKING.

private Vector3 WhereMyCameraIsLocated;
private Vector3 WhatMyCameraIsLookingAt;

void LateUpdate()
{
  cam.transform.position = WhereMyCameraIsLocated;
  cam.transform.LookAt( WhatMyCameraIsLookingAt);
}

Then you just need to update the above two points based on your GameObjects, no need to fiddle with rotations. As long as you move those positions smoothly, the camera will be nice and smooth as well, both positionally and rotationally.

Let me just preface this by saying DAMN! I’ve seen you everywhere, and you even comment first on my post?
Like I looked at forum stuff from years ago and noticed your cute profile picture.

Anyways, I appreciate your response, but I just really want this to be as custom as possible, I have a lot of other stuff going on in my cameracontroller script right now and I’d like to keep it all there if possible…

Do you think I could possibly alter my script to work the same way it does, but always maintain the camera’s roll in relation to the car?

I don’t know whether or not it would be possible to implement in a simple way, I just really would prefer to keep it working as it does, as it feels really good at the moment, but without having to learn how to use a new package and stuff…

Still though, I’m glad you showed it to me, if stuff doesn’t work out like this I’ll probably try it out and see if I can get what I want using it :slight_smile:

1 Like

Perhaps… but I think the problem you are seeing, the accumulation of a tilt like that, is coming from the fact that rotations performed again and again are not commutative, eg. it matters the order of doing the rotation.

One approach might be to modify that short little snippet I had above to use the version of LookAt() that also takes an up vector, and to feed the normal of the ground where the car is at for that vector. I think then it would look like:

private Vector3 WhereMyCameraIsLocated;
private Vector3 WhatMyCameraIsLookingAt;

void LateUpdate()
{
  Vector3 UpNormalOfGround = ... obtain this by Raycasting down...

  cam.transform.position = WhereMyCameraIsLocated;
  cam.transform.LookAt( WhatMyCameraIsLookingAt, UpNormalOfGround);
}

You would default UpNormalOfGround to Vector3.up, then replace it if you raycast to the ground.

To make it smoothly change as you hit a ramp, rather than jerking sharply to one side, you could something like this to smooth stuff out:

Smoothing movement between any two particular values:

https://discussions.unity.com/t/812925/5

You have currentQuantity and desiredQuantity.

  • only set desiredQuantity
  • the code always moves currentQuantity towards desiredQuantity
  • read currentQuantity for the smoothed value

Works for floats, Vectors, Colors, Quaternions, anything continuous or lerp-able.

The code: https://gist.github.com/kurtdekker/fb3c33ec6911a1d9bfcb23e9f62adac4

Yo thanks for the detailed answer! I’m so sorry for making you go through writing all that because I JUST found the simplest solution. I was trying out rotating around different vectors when I noticed that the issue only happened because the Vector3.up was being calculated from the cam’s vector and not the car. As soon as I changed it to target.up it worked perfectly…

        // Rotation
        if (Input.GetMouseButton(1)) // Right-click is pressed
        {
            float horizontalInput = Input.GetAxis("Mouse X") * rotationSpeed;
            float verticalInput = Input.GetAxis("Mouse Y") * rotationSpeed;

            // Rotate the camera around the target
            transform.RotateAround(target.position, target.up, horizontalInput);
            transform.RotateAround(target.position, transform.right, -verticalInput);
        }

BTW is there some way I can maybe mark this reply as the answer to my original question so that others can see it? idk…