Global_transform.basis leading to horizontal camera rotation issues

Godot Version

4.3

Question

Hi guys, I’m working on a game where you walk across a sphere made of other smaller spheres, here’s a screenshot of the game for context:

It’s been a real pain to program the character to move along the sphere always facing straight, I used global_transform.basis for this using the distance between sphere_center and the player to determine the rotation (sphere_center is the sphere that is right in the middle inside all of these smaller spheres). This is what the code looks like for this in the physics_process() function:

func _physics_process(delta):

	# Calculate the desired up direction (radial direction from planet center)
	var desired_up = (global_transform.origin - sphere_center).normalized()
	# Get the current forward direction (-Z is forward in Godot's convention)
	var current_forward = -global_transform.basis.z
	# Project the forward direction onto the plane perpendicular to desired_up
	var tangent_forward = (current_forward - current_forward.dot(desired_up) * desired_up).normalized()
	# Define the new basis axes
	var new_z = -tangent_forward           # Z-axis (backward), so -tangent_forward is forward
	var new_x = desired_up.cross(new_z).normalized()  # X-axis, perpendicular to Y and Z
	var new_y = desired_up                # Y-axis is the desired up direction
	# Set the new basis
	global_transform.basis = Basis(new_x, new_y, new_z)
	# Reset movement direction each frame
	var move_dir = Vector3.ZERO

	# Get input and adjust movement direction based on character’s orientation
	if Input.is_action_pressed("Right"):
		move_dir += transform.basis.x  # Local right
	if Input.is_action_pressed("Left"):
		move_dir -= transform.basis.x  # Local left
	if Input.is_action_pressed("Forward"):
		move_dir -= transform.basis.z  # Local forward
	if Input.is_action_pressed("Back"):
		move_dir += transform.basis.z  # Local backward
	if Input.is_action_pressed("Up"):
		move_dir += transform.basis.y  # Local up

	# Normalize the direction to prevent faster diagonal movement
	if move_dir.length() > 0:
		move_dir = move_dir.normalized()
	# Calculate the direction from the character to the sphere_center
	var to_center = (sphere_center - global_transform.origin).normalized()
	# Apply the gravity force towards the sphere_center
	gravity = to_center * gravity_strength * delta
	# Apply velocity and move the character
	velocity = move_dir * speed + gravity
	move_and_slide()

Next I added some rotation to the camera, and rotated the player with rotate_y(deg_to_rad(-event.relative.x * mouse_sensitivity)) for horizontal camera movement:

func _input(event):
	if event is InputEventMouseMotion:
		# Horizontal rotation: Rotate the character (and camera with it)
		rotate_y(deg_to_rad(-event.relative.x * mouse_sensitivity))
		# Vertical rotation: Rotate only the camera
		var pitch_change = -event.relative.y * mouse_sensitivity
		var current_pitch = camera.rotation_degrees.x
		var new_pitch = current_pitch + pitch_change
		# Clamp the pitch to prevent flipping (e.g., -80° to 80°)
		new_pitch = clamp(new_pitch, -90, 90)
		camera.rotation_degrees.x = new_pitch
	if event.is_action_pressed("Place"):  # Trigger sphere placement
		place_sphere()

This is where the problem lies. The player rotates differently in the horizontal axis depending on where it is positioned on the sphere. So on some parts of the sphere the camera rotates perfectly fine horizontally, on some parts it rotates slower or not at all, and on some parts the camera controls are inverted horizontally. I have no idea why this is happening. Maybe because the sphere is not perfectly round?

I tried a lot of things such as using camera.rotate_y(deg_to_rad(-event.relative.x * mouse_sensitivity)) instead, and using the camera’s direction as a reference for horizontal movement but although this fixes the rotation issue the movement is rougher and the player gets stuck between spheres a lot, I’m guessing it is because global_transform.basis is not determining the direction entirely anymore and the camera direction is interfering.

Any help would be much appreciated!