Weird camera behavior when rotating on Z axis

Godot Version

4.6.1 stable

Question

I’ve been working at a 3D camera controller for my game, and I wanted to add some headbob
similar to SCP: Containment Breach, and i’ve managed to make it somewhat work through a tutorial, but when moving the camera in a certain way, the camera inverts and affects the movement of the player. (The player starts walking backwards or diagonally instead of straight). I have tried certain ways to fix it aswell as looking into the forums, but I haven’t found a concrete answer, nor was I able to come up with a solution. Here’s the headbob code, the code itself is located in physics process:

if velocity.length() > 0 and is_on_floor():
		t_bob += delta * velocity.length() * float(is_on_floor())
		camera.transform.origin = _headbob(t_bob)
		camera.rotation.z = _headbob2(t_bob)
	else:
		t_bob = 0
		camera.transform.origin = lerp(camera.transform.origin, Vector3.ZERO, delta * 5)
		camera.rotation.z = lerp(camera.rotation.z, 0.0, delta * 5)

And here’s the headbob functions:

func _headbob(time) -> Vector3:
	var pos = Vector3.ZERO
	pos.y = sin(time * BOB_FREQ) * BOB_AMP
	return pos

func _headbob2(time):
	var pos = Vector3.ZERO
	pos.z = sin(time * BOB_FREQ / 2) * 0.025
	return pos.z

Also a video demonstrating the issues:

If needed, I can send the whole code.

That’s called Gimbal Lock. You need to rotate on the x and y axes separately. This article in the Godot Docs talks about it: Using 3D transforms — Godot Engine (latest) documentation in English

The best way to handle this is to do something like this:

Where the SpringArm3D rotates on the y-axis and the Camera3D rotates on the x-axis.

In the above example, I am actually rotating the Pivot nodes and applying the rotation to them SpringArm3D and Camera3D nodes.

func _physics_process(delta: float) -> void:
	horizontal_pivot.rotate_y(Controller.look.x)
	vertical_pivot.rotate_x(Controller.look.y)
	vertical_pivot.rotation.x = clampf(vertical_pivot.rotation.x,
		deg_to_rad(upwards_rotation_limit),
		deg_to_rad(downwards_rotation_limit)
	)

	spring_arm_3d.rotation.y = horizontal_pivot.rotation.y
	camera_3d.rotation.x = vertical_pivot.rotation.x

For full code, you can check out my Camera3D Plugin and Character3D Plugin.

Thank you for responding, had no idea those limitations existed until now. However, is this something that I have to use your plugin for? I wouldn’t mind if that’s the case, but I’m just wondering if there’s another solution to this. (Edit: I also noticed that this seems to be the code for the camera movement code, not the actual headbob. So I assume I only change that and not the code that I provided? I apologize if I’m asking too many questions.)

You can fix it yourself. I was just showing you an example. The headbob should not be affected. You can also find videos online for fixing gimbal lock.

Alright, got it. Thank you.