Gimbal lock when using Quaternion?

I restrict mouse movement both vertically and horizontally. All of the code below works but when I walk over my trigger area which is a long, flat cube object that is directly above the ground, my camera instantly spins forward to the yRotation clamp value of 45 degrees.

There is no other code in my camera script aside from initialized variables and an else statement which contains standard movement when I am not standing in the trigger. I have even taken out that code entirely along with other code to make sure they weren’t overlapping and the problem is still there.

// inside Update() in my mouse movement script
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;

xRotation -= mouseY;
yRotation += mouseX;

xRotation = Mathf.Clamp(xRotation, -90f, 90f);
yRotation = Mathf.Clamp(yRotation, -45f, 45f);

Quaternion.AngleAxis(xRotation, Vector3.right);
player.transform.rotation = Quaternion.AngleAxis(yRotation, Vector3.up);

I’m not sure if more information is needed, but I originally tried this having my Main Camera as a child of my Player but the issue was there, so now I am testing it with my Main Camera on its own. I spent a lot of time researching and removing my two transform.eulerAngles lines to prevent gimbal lock. I even used transform.rotation = Quaternion.Euler(xRotation, yRotation, 0f) which had the same problem.

Right now I use player.transform to rotate my player, but if I use cameraObj.transform, gimbal lock is gone but so is my clamping.

Hmmm … makes me wonder: is there any chance you are actually, I mean “physically” tripping over that trigger? Like an actual physics bump that topples over the object. :wink:
Can only happen if the object in question has a RigidBody component though.

Other than that I wonder why you do Quaternion.AngleAxis(xRotation, Vector3.right) twice?
I’d approach the issue such that you can just call Quaternion.Euler(xRotation, yRotation, 0f). It will NOT work right out of the box for you because you do need to consider rotation relative to player.transform.forward first - unless you really want to clamp the horizontal rotation to absolute -45 / +45 values.

The trigger is actually invisible so it has no mesh renderer and no rigidbody or collider (aside from one with trigger on).

I use Quaternion.AngleAxis(xRotation, Vector3.right); and Quaternion.AngleAxis(yRotation, Vector3.up); but I’m not sure if you mean that they are essentially the same thing.

And in terms of considering the rotation relative to player.transform.forward, I actually have code inside my trigger which currently is commented out, which uses Quaternion.RotateTowards and Quaternion.LookRotation to turn my player towards the trigger (which works), and then once RotateTowards has completed via coroutine (almost an instant rotate), then my movement begins which is where the clamping comes in.

Or on top of that do I still need to find a way to consider the rotation like:

forward = transform.forward; // get the forward position when first entering trigger
forward.y = 0;
forward.Normalize();

I meant the player. It’s possible that the player’s rigidbody collides and physically interacts with the trigger. I’m not sure exactly which combo ignores collisions with trigger colliders and which don’t but I happen to have that issue with Physics.Raycast where a trigger collider blocked my player.

No but you are first rotation one axis, then another. The result may be different that rotating around both axis at the same time. Just a guess though, I know that order of operations matter for translate, rotate and scale (eg scale, rotate, translate will give you a different result) and I vaguely remember the same might be true for subsequent Quaternion rotations.

I currently don’t have a rigidbody as my player is using a character controller.

I think I understood what you meant, I am trying to get the “forward” position when I enter the trigger so that I can use that for clamping, but now with my updated code, there’s no clamping at all, but the gimbal lock is gone.

forward = transform.forward; // get the forward position when first entering trigger
forward.y = 0;
forward.Normalize();

float lookAngle = Vector3.Angle(Vector3.forward, forward);
yRotation = Mathf.Clamp(yRotation, -35f + lookAngle, 35f + lookAngle);
xRotation = Mathf.Clamp(xRotation, -90f, 90f);

transform.rotation = Quaternion.Euler(xRotation, yRotation, 0f);
player.transform.Rotate(Vector3.up * mouseX);

I have a hunch that transform.Rotate() may interfere. I always directly set the rotation from the Quaternion in character controllers.

By setting the rotation you mean what I have above? transform.rotation = Quaternion.Euler(xRotation, yRotation, 0f);