Rotation getting stuck / glitchy at 90 and -90 deg on the x axis

Godot Version

v4.5.stable.flathub [876b29033]

Question

Hey, new Godot user here. I have used a couple tutorials to get a little 3D block game going (mostly the LegionGames minecraft series). I have a background in programming, but there is a lot of stuff specific to games that is going over my head right now.

In order to teach myself better I didn’t follow the tutorials a 100% but introduced variations so I couldn’t just copy+paste stuff and had to really understand. Now my mouse-driven camera rotation is messed up in a creative way.

In short, this is what the problem looks like:

asteroid-miner-90deg-glitch

For this test, I just dragged the mouse forward to look up. No movement or anything else. It seems to be hitting a weird boundary.

I want full, fluid rotation with no clamping because I made it space-themed and my character is floating in space.

It seems to be a transform issue or some basic math I don’t get with 3D rotation. I tried many example of rotation transforms but every single one of them gave the swaying / unraveling motion of getting the axis order wrong, but… in every order. I copied word for word the rotation stuff from different tutorials to figure it out… to no effect. In the end, the only version that works only uses the rotation prop I know it’s bad, nothing else works! not the official doc examples either!

scripts/player.gd assigned to CharacterBody3D node

const MOUSE_SENSITIVITY =  0.005

# more code ...

func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseMotion:
		rotation.y -= event.relative.x * MOUSE_SENSITIVITY
		rotation.x -= event.relative.y * MOUSE_SENSITIVITY

It seems absurd to me that directly copying examples produces vastly different outcomes, with the transforms failing altogether. My best guess is that I might be handling movement wrong, as it is the only other place in the entire project where I deal with transforms and positioning stuff.

same script

const THRUST_SPEED = 0.05

# more code ...

var horizontal_input: Vector2 = Input.get_vector("left", "right", "forward", "backward")
var vertical_input: float = Input.get_axis("down", "up")
var direction: Vector3 = (transform.basis * Vector3(horizontal_input.x, vertical_input, horizontal_input.y)).normalized()

if direction:
		velocity += direction * THRUST_SPEED

Is anything obviously wrong to someone? A basic misunderstanding somewhere? Tell me if there are other bits of code or setup that should be shared / are relevant.

Thanks!

When you hit that “boundry” it’s because you go “upside down” and your local rotation x goes from positive to negative, or something like that… Printing your rotation might give you an idea of what’s going on there.
Anyway, since your rotation goes negative, substracting the event relative form it will make it rotate the other way for that frame, that’s why it’s twitching back and forth at the “edge” of being upside down and not being upside down again.

I think you can prevent that if you rotate like this:

rotate_y(-event.relative.x * MOUSE_SENSITIVITY)
rotate_x(-event.relative.y * MOUSE_SENSITIVITY)

But this will probably not give you the wanted results either. Doing full 360 free rotation where you can go upside down and all that is kind of tricky, I’m not sure how to do it properly…

1 Like

Actually just doing something like this might be better:

	if event is InputEventMouseMotion:
		rotation.y -= event.relative.x * MOUSE_SENSITIVITY
		if 0.0 < rotation.x:
			rotation.x -= event.relative.y * MOUSE_SENSITIVITY
		else:
			rotation.x = event.relative.y * MOUSE_SENSITIVITY
1 Like

Thanks for the reply. Sadly the rotate_x() and rotate_y() were part of my experiment, but failed. While there is no boundary when I use them, I get one of those weird Euler effects where the axes kinda desync from each other. I ran you exact code to show what I mean:

rotate_y(-event.relative.x * MOUSE_SENSITIVITY)
rotate_x(-event.relative.y * MOUSE_SENSITIVITY)

Capturevidodu2025-09-2420-29-48-ezgif.com-optimize
Here I am just dragging the mouse left to rotate horizontally.

Curiously, calling rotate_x before rotate_y gives similar results, even though the order should matter.

That doesn’t happen either. I can replace my code with:

rotation.x = deg_to_rad(120)
print(rad_to_deg(rotation.x))

And it works, I go over the boundary and stay pinned there.

Solution n°2 isn’t it either but in a curious turn of events, it creates a second boundary at 0° :sweat_smile:

It gives me something to think about.

Ok, I isolated the rotation.x and updated only that, it works. Of course I can’t turn left or right but it means that the y rotation is the culprit. It somehow resets the rotation.x at 90 everytime it goes over :grimacing:

Edit: testing with only changing rotation.y for rotate_y gives me the same result. The transforms are somehow not helping, unless I use only them, in which case I get my axes out of sync.

(doc for the transforms: Using 3D transforms — Godot Engine (stable) documentation in English)

I solved this… and figured out I had a very basic misunderstanding. The correct behavior did not turn out to be the one I wanted.

First, the fix (it was in the doc all along):

# mistake n°1: don't use the rotation vector like an accumulator.
# you need to accumulate rotation angles on their own if you want
# to reset the transform every time.
var angle_accumulator: Vector2 = Vector2.ZERO

# more code ...

# in the input catching function
angle_accumulator.x -= event.relative.x * MOUSE_SENSITIVITY
angle_accumulator.y -= event.relative.y * MOUSE_SENSITIVITY
# reset the transform to keep your axes aligned with the world
transform.basis = Basis()
# rotate_object_local works here where rotate doesn't 🤷‍♀️
rotate_object_local(Vector3(0, 1, 0), angle_accumulator.x)
rotate_object_local(Vector3(1, 0, 0), angle_accumulator.y)

Done! Hope it can help someone, I had a hard time fishing for answers suited for beginners on this one.


As for me… The problem was thinking I could orient myself in 3D space while only controlling 2 axes with the mouse.

Simple scenario: if I want left to mean left no matter my orientation, then when I look at my feet, going directly left is gonna leave the floor to my right. The transforms were mostly accurate, and the abnormal situation would have been the floor being still down after those rotations.

If I want to orient myself freely in 3D space I need to control the 3rd axis manually.

I think I’m gonna add the mouse wheel to roll left / right. At least until I understand this better :sweat_smile: I’ll probably look at what flight simulators are doing too.

So that’s my big takeaway… A mouse does not behave like a joystick.

Thanks for the help!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.