I am making a game where you can control a fast moving rocket using the mouse (like you look around in an fps)

func _physics_process(delta : float) -> void:
	position += transform.basis * Vector3(0, 0, -SPEED) * delta #constantly move in the pointing direction

func _unhandled_input(event : InputEvent) -> void:
	if event is InputEventMouseMotion:
		rotate_y(-event.relative.x * SENSITIVITY) #rotate vertically according to mouse movement
		rotate_x(-event.relative.y * SENSITIVITY) #rotate horizontally according to mouse movement
		transform = transform.orthonormalized()

The problem is after a couple of seconds the controls don't behave normally, and the doesn't rotate normally.
I read about this issue in the docs regarding rotation, which is why I added transform.orthonormalized() but it doesn't solve the issue.
Should I do the rotating inside _process ?
I am bad at Vector math and rotations, so can anybody point me in the right direction ?
(I can post a video of the weird behaviour if it helps)

You can try to convert deg_to_rad and see if rotation improve,
I'm using this for mine Camera 3D but on different project because involve also the movements.
First i am capture the mouse inside the window.
Then i add a spring/camera attach to the player on another node called "mount".
This fix the rotation on the model.
I guess you can do the same by rotation the scenario only
Not sure if this will help, hope still it gives you a direction on the solution.
(mini related discussion have other formula and infos)

func _unhandled_input(event: InputEvent) -> void:
 if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
       mount.rotate_y(deg_to_rad( - event.relative.x * mouse_sensitivity))
       spring.rotate_x(deg_to_rad( - event.relative.y * mouse_sensitivity))

@fatalox I don't see how deg_to_red could possibly help here. That just switches the value to a different scale.

@Bilal Your problem description is a big vague. But it seems you may be encountering a Gimbal Lock (https://en.wikipedia.org/wiki/Gimbal_lock).

In Godot this is easily avoided by never rotating a node around more than one axis. If you need both x and y rotation, use one node for the y rotation and then add a child node that handles the x rotation.

    Zini In my case using radiant have improved my understanding by a lot because then i was able to manipulating better by, as you also say, handling the two rotation with different nodes.

    • Zini replied to this.

      Try first with only X axis, can be enough for your game

        Zini How would using 2 different nodes look like ? Do you mean calling get_parent().rotate ... from the child's node ? I need to rotate two different axes simultaniously.

        fatalox I noticed something strange. When I only allow turing horizontally, it works. But when I only allow turning vertically, it still causes weird issues. And I noticed that rotatex() rotates vertically and rotate_y rotates horizontally.

          Bilal I notice the same during mine project at beginning.
          I place the X axis inside a node an everything improved, while keeping Y axis without.
          Then i clamp the X (and Z axis later for other needs) for having a limit on my rotation.
          You should describe better your issues, your project.
          What you trying to achieve by your rocket? Do you wanna do a full 180° vertical spin for changing direction?
          My working was planned all on two legs human, so probably different!

          Zini I used two nodes and that made it even worse, but it's probably my implementation that is wrong

          • Zini replied to this.

            fatalox Doesn't make any sense to me, since deg_to rad(x) is basically the same as x*0.017453292519943295. But that is probably besides the point here anyway.

            Bilal Need more information. Scene structure maybe? And your new script?

              Zini So I kind of fixed it by limiting the axes of rotation to one like you said. I look at the mouse movement and if the x movement is bigger I turn horizontally, else I turn vertically. Now this fixed turning horizontally, but vertically, is like a mix between rotating around Z and actually around Y.

              func _unhandled_input(event : InputEvent) -> void:
              	if event is InputEventMouseMotion:
              		transform = transform.orthonormalized()
              		if abs(-event.relative.x * SENSITIVITY) > abs(-event.relative.y * SENSITIVITY):
              			rotate_y(-event.relative.x * SENSITIVITY)
              		else:
              			rotate_x(-event.relative.y * SENSITIVITY)
              		transform = transform.orthonormalized()

              As for my solution using two nodes I rotate_y in my rocket scene and send a signal to a child scene which rotate_x's the rocket scene , Scene structure:
              rocket(main script)
              ----MeshInstance3D
              ----CollisionShape3D
              ----Camera3D(child script)
              main script:

              signal rotatex(amount : float)
              func _unhandled_input(event : InputEvent) -> void:
              	if event is InputEventMouseMotion:
              	        rotate_y(-event.relative.x * SENSITIVITY)
                              rotatex.emit(-event.relative.y * SENSITIVITY)
              		transform = transform.orthonormalized()

              child script:

              @onready var parent = get_parent()
              func rotatx(amount : float):
                  parent.rotate_x(amount)
                  parent.transform = parent.transform.orthonormalized()

              If there are syntax errors, they are from typing it here, not in the actual code.

              There is no need to use a signal. Or to have a script on the child node at all. Also in the child script you manipulate the rotation of the parent, which gets you back to rotating the same node around two axis.

              Try replacing

              rotatex.emit(-event.relative.y * SENSITIVITY)

              with

              $Camera3D.rotate_x(-event.relative.y * SENSITIVITY)

              Edit: Actually, that would rotate the camera in both direction and the actual model only in one. That is probably not what you want? In this case we need to tweak the node structure more. But its a starting point anyway.

                Zini Yes, as you realized I want to rotate the same node around two axes. But there is something wrong with rotate_x(-event.relative.y * SENSITIVITY) even when it's running alone. Maybe I'll do something different alltogether.

                No you don't want to rotate a node around two axis. That will never work properly. You want two nodes. Insert a dummy node, if you must. In your case you could insert a Spatial node (or Node3d in Godot 4) above your main node and make that your new main node. Also move the script to that. You rotate your new main node around one axis and your previous main node around the other. Visually the result should be exactly the same as with one node, but without all the problems.

                Edit: The problem with two axis on one node is of course only present if you work with Euler rotation. But the alternative is even more complicated: https://docs.godotengine.org/en/stable/tutorials/3d/using_transforms.html. Using two nodes is the way to go here.

                  Zini Now rotation works fine, but beacause my movement code relies on the transfrom of my rocket, when rotating the other node, I actually only "look" up, but still move in the same direction.

                  If the top level node isn't attached to any nodes that do any transforms of their own you can use the global_transform of the 2nd node (your previous main node) instead of the transform of the new main node.

                    Zini I actually kept my old main node "main" and instead added a new node3d which I rotate.
                    rocket(main scene)
                    ----node3d(to handle 3nd axe)
                    --------mesh and camera
                    Looks like I can't escape rotating a transform around two axes