Camera Rotation problems with gamepad

Godot Version

4.3 stable

Question

I am currently trying to set up a camera for a third person controller using the right joystick of a gamepad. I have been only able to find one post where someone talks about how they had to figure it out. Their example is in C# so I modified it to work in gdscript.

The Camera3D is a child of a SpringArm3D and i am specifically rotating the SpringArm. The problem is i can look up and down using the x-axis just fine when i first load the scene. but if i rotate the camera on the y-axis AT ALL, the spring arm and in turn camera start to spin to the side as if it is rotating on the z-axis.

so after being stumped i rubber ducked to walk through the logic of the rotation. And from my understanding after testing rotations on a simple cube, I was not rotating the spring arm locally but i was globally. So if i changed my code to be local rotation then the x&z axis would rotate along with the camera whenever i rotated along the y-axis…

only to look at the GDScript documentation for rotate_y() to see that I have been using local rotation functions this entire time…

again this code is almost entirely from someone else. just translated from C# to gdscript.

the funny thing is, if i set the z rotation of the camera controller to be 0, then launch my scene and rotate my camera 90 degrees to any side and push up to rotate on the x-axis the camera does not budge. so somehow in all of this code the rotate_y & rotate_x functions are global instead of local, contrary to what the documentation suggests. And because of that i am rotating on the z-axis even though nowhere in my code i am changing the value of the z-axis.

I just wanted controller support for my third person character, but every single tutorial and or forum post besides one i’ve found uses mouse as the example. this is a brick wall i do not know how to cross.

Edit: i found the link to the original post
https://forum.godotengine.org/t/third-person-controller-with-joystick/55289


func _physics_process(delta: float) -> void:	
	axis_vector.y = Input.get_action_strength("camera_right") - Input.get_action_strength("camera_left")
	axis_vector.x = Input.get_action_strength("camera_down") - Input.get_action_strength("camera_up")
	if axis_vector.length() >= 0.2:
		camera_controller.rotate_y(deg_to_rad(-axis_vector.y * 1.5))
		camera_controller.rotate_x(deg_to_rad(-axis_vector.x * 1.5))
        #camera_controller.rotation.z = 0
		camera_controller.rotation = Vector3(clamp(camera_controller.rotation.x, deg_to_rad(-45), deg_to_rad(45)), camera_controller.rotation.y, camera_controller.rotation.z)
		print(str(camera_controller.rotation.z))

Most use a pivot node between the camera or in your case spring arm.

The pivot can rotate_y and the springarm can rotate_x without getting gimbal locked or two-step z rotations

@onready var camera_pivot = $CameraPivot
@onready var camera_controller = $CameraPivot/SpringArm3D

# ...
camera_pivot.rotate_y(deg_to_rad(-axis_vector.y * 1.5))
camera_controller.rotate_x(deg_to_rad(-axis_vector.x * 1.5))

and of course go ahead and use Input.get_vector for your inputs

var axis_vector = Input.get_vector("camera_left", "camera_right", "camera_up", "camera_down")
1 Like

I may have misunderstood or something else may be wrong. I put a Node 3D as a child of the camera controller and made my camera a child of that node. so it is now CameraController/CameraPivot/Camera3D. I then implemented your code, i did catch that you said to use get_vector() when your example said get_axis() so i avoided that issue but it now my camera problems seem far worse. the gif is just me pushing up on my joystick. did I mess something up or could something else be happening? my camera controller is at top level so my player movement does not affect it’s rotation, but i do have a simple lerp to have the camera follow the player
camera rotation

var axis_vector = Input.get_vector("camera_left", "camera_right", "camera_up", "camera_down")
	if axis_vector.length() >= 0.2:
		camera_pivot.rotate_y(deg_to_rad(-axis_vector.y * 1.5))
		camera_controller.rotate_x(deg_to_rad(-axis_vector.x * 1.5))

I’m guessing the CameraController is your spring arm? Put the spring arm as a child of the pivot.

1 Like

yes my spring arm is CameraController. so now i have CameraPivot(Node3d)/CameraController(SpringArm3D)/Camera3D. after testing i …

I was gonna say there was an entirely new problem but i then realized multiple things. since i switched the camera pivot to be the parent i had to turn off top level on my spring arm and turn it on for the pivot. i then had to change some other code that sets the direction of my player relative to the position of the camera to use the camera pivot instead of the spring arm. and after changing all of that code I also realized the inputs were mismatched so i put axis_vector.x for camera_pivot.rotate_y and the vector.y for the controller.rotate_x.

and now after changing all of that i do think my problem is officially solved! thank you for the help!

Code for reference:

var axis_vector = Input.get_vector("camera_left", "camera_right", "camera_up", "camera_down")
	if axis_vector.length() >= 0.2:
		camera_pivot.rotate_y(deg_to_rad(-axis_vector.x * 1.5))
		camera_controller.rotate_x(deg_to_rad(-axis_vector.y * 1.5))
#...
#new vector3 direction taking into account the user inputs and camera location
	var direction := (camera_pivot.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	
	#rotate character mesh to face direction of movement
	if input_dir != Vector2(0,0):
		body_mesh.rotation_degrees.y = camera_pivot.rotation_degrees.y - rad_to_deg(input_dir.angle()) - 90
#...
#make camera controller match the pos of myself
	camera_pivot.position = lerp(camera_pivot.position, Vector3(position.x, position.y + 2.0, position.z), 0.15)
1 Like

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