Inverted rotation at certain angles

Hi, I have an object that rotates on it’s X axis using a rigidbody.
I would like to be able to use mouse input to rotate it on the Y axis.
I wrote a function called Tilt, getting called in Update, that does it for the most part:

private void Tilt()
{
    var amount = Input.GetAxisRaw("Mouse X") * _tiltSpeed;
    transform.Rotate(Vector3.up , amount, Space.World);
}

However, as can be seen in this video, input gets inverted when you go upside down.


My first thought was to just invert amount if X rotation is over 180 or below -180(the upside down state).

private void Tilt()
{
    var ea = transform.eulerAngles;
    var amount = Input.GetAxisRaw("Mouse X") * _tiltSpeed;

    amount *= ea.x >= 180 ? -1 : 1;
    amount *= ea.x <= -180 ? -1 : 1;
    transform.Rotate(Vector3.up, amount, Space.World);
}

This works for the most part, except for when it doesn’t.
As can be seen in this video, there are edge cases in which the rotation still gets inverted and quickly switches between being normal and inverted, resulting in jitter instead of rotation.


I’ve been trying to debug this one for a few days now and just can’t figure it out.
The expected behaviour would be pretty much what’s seen in the first video, except for the part where it gets inverted when the object is rotated at a certain angle.
I was suggested that this could be a gimbal lock issue, but I don’t see how, I am not using euler angles anywhere, am I wrong?

The quick flipping you experienced in your second example would be the issue where a euler angle can represent multiple rotations, as you’re using eulers in that example.

What might be more reliable that rotations - which in Unity are handled with quaternions, not euler angles - would be to test the dot product of the camera’s ‘up’ with world up: Unity - Scripting API: Vector3.Dot

A value greater than zero means you’re upright, and less than zero means you’re upside down. Then you can reverse the delta, or even reverse the axis of rotation if so.

Mind you, if this object is being controlled by a rigidbody, then you should be rotating it via the rigidbody as well. This would mean taking the rigidbody’s rotation, and combining it with another rotation you generate, likely Quaternion.AngleAxis in this case.

Reminder that Unity rotation values in the inspector are only eulers for human readability. They may not accurately represent the actual rotation under the hood.

1 Like

You nailed it with the Quaternion.AngleAxis, which I combined with Rigidbody.MoveRotation as per your recommendation.
This is the final form of the function(with other axis rotation included):

private void Tilt()
{
    var x = Input.GetAxisRaw("Mouse X") * _tiltSpeed;
    var y = Input.GetAxisRaw("Mouse Y") * _tiltSpeed;
    var deltaRotX = Quaternion.AngleAxis(x, Vector3.back);
    var deltaRotY = Quaternion.AngleAxis(y, Vector3.left);
    var deltaRot = deltaRotX * deltaRotY;
    _rb.MoveRotation(_rb.rotation * deltaRot);
}

It actually makes the movement feel exactly how I imagined it, thanks a ton!

1 Like