0

So I have this setup:

  1. Camera rotation is clamped on the Y axis. It can only rotate from -75 to 75 degrees left and right, and its rotation is controlled by the mouse.

  2. Player body can rotate around its Y axis, and its Y rotation value is added to -75 and 75 degree camera clamps in order to keep the camera rotation space in front of the player body. Example: If the player rotates 90 degrees on the Y axis, the clamp values would change to 15 and 165 degrees.

The problem arises when the player body exceeds 359 degrees on the Y axis because camera clamp values jump from 284 and 434 back to -75 and 75, which causes a noticeable snap in camera movement. How can I eliminate this snap?

Video example - Note the camera clamps (Limit L, Limit R) in the MouseLook script after 00:40

*The handleBodyRotation method just rotates the player body to face the red cube which is a child of the camera.

public class MouseLook : MonoBehaviour
{
    public Transform CameraBox;
    public Transform lookAtPivot;
    public Transform lookAtObject;
    public Transform playerBody;
 
    public float sensitivity;
    public float bodyRotSpeed;
 
    float xRotation = 0f;
    float yRotation = 0f;
    public float limitL;
    public float limitR;
 
    void Start()
    {
        Cursor.lockState = CursorLockMode.Locked;
    }
 
    void handleCamRotation()
    {
        float mouseX = Input.GetAxis("Mouse X") * sensitivity * Time.deltaTime;
        float mouseY = Input.GetAxis("Mouse Y") * sensitivity * Time.deltaTime;
 
        xRotation -= mouseY;
        xRotation = Mathf.Clamp(xRotation, -45, 35);
 
        yRotation += mouseX;
        yRotation = Mathf.Clamp(yRotation, limitL, limitR);
 
        //Handle and clamp Camera, lookAtPivot, and lookAtObject rotation
        transform.rotation = Quaternion.Euler(xRotation, yRotation, 0f);
 
        //Handle left limit and right limit
        limitL = -75 + playerBody.transform.rotation.eulerAngles.y;
        limitR = 75 + playerBody.transform.rotation.eulerAngles.y;
       
    }
 
    void handleBodyRotation()
    {
        Quaternion targetRotation = Quaternion.Euler(playerBody.transform.rotation.eulerAngles.x, lookAtObject.transform.rotation.eulerAngles.y, playerBody.transform.rotation.eulerAngles.z);
        playerBody.transform.localRotation = Quaternion.Lerp(playerBody.transform.localRotation, targetRotation, bodyRotSpeed * Time.deltaTime);
    }
 
    void Update()
    {
        //Camera Rotation (clamped)
        handleCamRotation();
 
        //Body Rotation
        if (Input.GetKey(KeyCode.W))
        {
            handleBodyRotation();
        }
    }
}
dbc
  • 104,963
  • 20
  • 228
  • 340

1 Answers1

0

Your problem is the value of Transform.eulerAngles

When you read the .eulerAngles property, Unity converts the Quaternion's internal representation of the rotation to Euler angles. Because, there is more than one way to represent any given rotation using Euler angles, the values you read back out may be quite different from the values you assigned. This can cause confusion if you are trying to gradually increment the values to produce animation.

In particular the eulerAngles will always return a value between 0 and 359.9999... !

You say yourself it jumps to 284 and 434 and then back to -75 and 75.

Why?

Because

  • 359 - 75 is 284 and 359 + 75 is 434
  • 0 - 75 is -75 and 0 + 75 is 75.

Your eulerAngles.y value was somewhere close to the wrap point at 359.999... <-> 0.


I think you would have to wrap around these values like e.g.

limitL = -75 + playerBody.transform.rotation.eulerAngles.y;
limitR = 75 + playerBody.transform.rotation.eulerAngles.y;

if(limitR > 360) limitR -= 360;
if(limitR < 0) limitR += 360;

if(limitL > 360) limitL -= 360;
if(limitL < 0) limitL += 360;
  

And not sure but I guess you also would have to clamp like

if(limitR > limitL)
{
    yRotation = Mathf.Clamp(yRotation, limitL, limitR);
}
else
{
    yRotation = Mathf.Clamp(yRotation, limitR, limitL);
}

Don't know how Unity handled that case .. might be redundant.


Note: Typed on smartphone but I hope the idea gets clear

derHugo
  • 83,094
  • 9
  • 75
  • 115
  • Thank you for your response @derHugo but unfortunately that solution didn't work, I'm guessing because the clamps still depend on the playerBody eulerAngles.y and so when that goes over 360 I get similar results. Is there any other way I could return or track playerBody Y rotation over 360 degrees as while having the ability to increment it with my constants (-75 and 75)? – Mr. Dingleberry Feb 10 '21 at 18:31