Top-down twin stick shooter rotation in 3D with joystick or gamepad

Godot Version

v4.2.2.stable.official [15073afe3]

Question

Hi there!

I am creating a top-down 3D project, and was hoping to get some help on how to most correctly rotate my player character for this kind of game.

I have been reading a lot about the transforms and that I perhaps should ignore concerning myself with angles, so that is the context I am coming here with.

Here is my code so far. I am wondering:

  • Is using looking_at the right approach? I want to look with right analog stick, but move with the left analog stick
  • How do I prevent the turns from being very snappy? I would like to be able to adjust the “turn speed”

Thank you for any help you may provide!

extends CharacterBody3D

@export var speed: float = 10.0
@onready var player_mesh = $CollisionShape3D

func _ready():
	Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

func _physics_process(delta):
	var movement_direction = Input.get_vector("left_gamepad_left", 
										"left_gamepad_right", 
										"left_gamepad_forward", 
										"left_gamepad_backward")
	var rotation_direction = Input.get_vector("right_gamepad_left",
										"right_gamepad_right",
										"right_gamepad_forward",
										"right_gamepad_backward")
	var player_direction = (transform.basis * delta * Vector3(movement_direction.x, 0, movement_direction.y)).normalized() 
	var player_rotation = (transform.basis * delta * Vector3(rotation_direction.x, 0, rotation_direction.y)).normalized() 
	if player_direction:
		velocity.x = player_direction.x * speed
		velocity.z = player_direction.z * speed
	else:
		velocity.x = 0.0
		velocity.z = 0.0
	if player_rotation != Vector3.ZERO:
		player_mesh.basis = Basis.looking_at(player_rotation)
		player_mesh.basis.orthonormalized()
	move_and_slide()

Here is how I would solve your problem , it might not work and there are probably better ways to do it

	var turnspeed = delta*10
	if $player_mesh.global_rotation_degrees.y - rad_to_deg(player_rotation) >=180 or $player_mesh.global_rotation_degrees.y - $player_mesh.global_rotation_degrees.y - rad_to_deg(player_rotation) <=-180:
		turnspeed = delta*-10
	else:
		turnspeed = delta*10
	$player_mesh.global_rotation.y = move_toward($player_mesh.global_rotation.y ,player_rotation, turnspeed)
	

Hopefully this helps

1 Like

Thank you for your help, dude!

1 Like