Hello, I'm new to Godot and wanted to share what took me a couple days to scrape together, but I finally managed to get a working tank controls movement script for version 4.1 for player movement, such as what's used in the classic RE games or Silent Hill. It uses relative positioning to determine where to move and rotate the player.


extends CharacterBody3D
 
@export var FORWARD_SPEED = 2.0
@export var BACK_SPEED = 1.0
@export var TURN_SPEED = 0.025

var Vec3Z = Vector3.ZERO

#OPTIONAL: These could be used to change sensitivity of either rotating z or y
#var M_LOOK_SENS = 1
#var V_LOOK_SENS = 1

func _physics_process(delta: float) -> void:
	if Input.is_action_pressed("move_forward") and Input.is_action_pressed("move_back"):
		velocity.x = 0
		velocity.z = 0

	elif Input.is_action_pressed("move_forward"):
		var forwardVector = -Vector3.FORWARD.rotated(Vector3.UP, rotation.y)
		velocity = -forwardVector * FORWARD_SPEED
		
	elif Input.is_action_pressed("move_back"):
		var backwardVector = Vector3.FORWARD.rotated(Vector3.UP, rotation.y)
		velocity = -backwardVector * BACK_SPEED
	
	#If pressing nothing stop velocity
	else:
		velocity.x = 0
		velocity.z = 0
	
	# IF turn left WHILE moving back, turn right
	if Input.is_action_pressed("turn_left") and Input.is_action_pressed("move_back"):
		rotation.z -= Vec3Z.y + TURN_SPEED #* V_LOOK_SENS
		rotation.z = clamp(rotation.x, -50, 90)
		rotation.y -= Vec3Z.y + TURN_SPEED #* M_LOOK_SENS
	
	elif Input.is_action_pressed("turn_left"):
		rotation.z += Vec3Z.y - TURN_SPEED #* V_LOOK_SENS
		rotation.z = clamp(rotation.x, -50, 90)
		rotation.y += Vec3Z.y + TURN_SPEED #* M_LOOK_SENS

	# IF turn right WHILE moving back, turn left
	if Input.is_action_pressed("turn_right") and Input.is_action_pressed("move_back"):
		rotation.z += Vec3Z.y - TURN_SPEED #* V_LOOK_SENS
		rotation.z = clamp(rotation.x, -50, 90)
		rotation.y += Vec3Z.y + TURN_SPEED #* M_LOOK_SENS
		
	elif Input.is_action_pressed("turn_right"):
		rotation.z -= Vec3Z.y + TURN_SPEED #* V_LOOK_SENS
		rotation.z = clamp(rotation.x, -50, 90)
		rotation.y -= Vec3Z.y + TURN_SPEED #* M_LOOK_SENS
	
	move_and_slide()

Hopefully someone can find this helpful!

    a month later

    Hey ChiefSquidlam !

    So I've been having a crack at using this and, other than needing to reverse movements*, I like the way it works!

    *Not sure what this is about as I have the player facing the right way ie. -z. If I rotate them then that messes up the directions even more...!

    I've been tinkering and I've almost got animations working for it (imported Blend files using Mixamo animations) so I'll share my tweaks to your code so far, with the following caveats:

    • The idle animation placement means that turn animation won't play, since the character isn't moving if they rotate, so I need to figure out where/how best to rearrange this - I'm guessing an 'idle' var set to true/false, maybe?

    • Run animation (forward and back) only plays the first frame, even though the player moves okay. I'm honestly not entirely sure why this happens - tried setting the animation to loop and non-looped and it still happens.

    • Turn and backward (walk and run) animations, even if exported as 'in place', have trouble synching with movement speed

    In short: walk and idle animations work perfectly. Here's the code:

    extends CharacterBody3D
     
    @export var FORWARD_SPEED = 2.0
    @export var BACK_SPEED = 5.0
    @export var TURN_SPEED = 0.025
    @export var RUN_SPEED = 4.0
    @export var BACK_RUN_SPEED = 3.0
    
    @onready var animation_player = $AnimationPlayer
    
    var Vec3Z = Vector3.ZERO
    
    #OPTIONAL: These could be used to change sensitivity of either rotating z or y
    #var M_LOOK_SENS = 1
    #var V_LOOK_SENS = 1
    
    #all directions had to be reversed for some reason ie. move_back = forward, left = right, etc
    
    func _physics_process(delta: float) -> void:
    	if Input.is_action_pressed("move_forward") and Input.is_action_pressed("move_back"):
    		velocity.x = 0
    		velocity.z = 0
    
    	elif Input.is_action_pressed("move_back"):
    		var forwardVector = -Vector3.FORWARD.rotated(Vector3.UP, rotation.y)
    		velocity = -forwardVector * FORWARD_SPEED
    		if animation_player.current_animation != "Walk_Back":
    			animation_player.play("Walk_Back")
    		
    	elif Input.is_action_pressed("move_forward"):
    		var backwardVector = Vector3.FORWARD.rotated(Vector3.UP, rotation.y)
    		velocity = -backwardVector * BACK_SPEED
    		if animation_player.current_animation != "Walk":
    			animation_player.play("Walk")
    	
    	#If pressing nothing stop velocity
    	else:
    		velocity.x = 0
    		velocity.z = 0
    		if animation_player.current_animation != "Idle":
    			animation_player.play("Idle")
    	
    	# IF turn left WHILE moving back, turn right
    	if Input.is_action_pressed("turn_right") and Input.is_action_pressed("move_forward"):
    		rotation.z -= Vec3Z.y + TURN_SPEED #* V_LOOK_SENS
    		rotation.z = clamp(rotation.x, -50, 90)
    		rotation.y -= Vec3Z.y + TURN_SPEED #* M_LOOK_SENS
    	
    	elif Input.is_action_pressed("turn_right"):
    		if animation_player.current_animation != "Turn_Right":
    			animation_player.play("Turn_Right")
    		rotation.z += Vec3Z.y - TURN_SPEED #* V_LOOK_SENS
    		rotation.z = clamp(rotation.x, -50, 90)
    		rotation.y += Vec3Z.y + TURN_SPEED #* M_LOOK_SENS
    		
    		
    
    	# IF turn right WHILE moving back, turn left
    	if Input.is_action_pressed("turn_left") and Input.is_action_pressed("move_forward"):
    		rotation.z += Vec3Z.y - TURN_SPEED #* V_LOOK_SENS
    		rotation.z = clamp(rotation.x, -50, 90)
    		rotation.y += Vec3Z.y + TURN_SPEED #* M_LOOK_SENS
    		
    	elif Input.is_action_pressed("turn_left"):
    		if animation_player.current_animation != "Turn_Left":
    			animation_player.play("Turn_Left")
    		rotation.z -= Vec3Z.y + TURN_SPEED #* V_LOOK_SENS
    		rotation.z = clamp(rotation.x, -50, 90)
    		rotation.y -= Vec3Z.y + TURN_SPEED #* M_LOOK_SENS
    		
    	
    	
    	#running	
    	if Input.is_action_pressed("move_back") and Input.is_action_pressed("run"):
    		var forwardVector = -Vector3.FORWARD.rotated(Vector3.UP, rotation.y)
    		velocity = -forwardVector * RUN_SPEED
    		if animation_player.current_animation != "Run":
    			animation_player.play("Run")
    		
    	elif Input.is_action_pressed("move_forward") and Input.is_action_pressed("run"):
    		var backwardVector = Vector3.FORWARD.rotated(Vector3.UP, rotation.y)
    		velocity = -backwardVector * BACK_RUN_SPEED
    		if animation_player.current_animation != "Run_Back":
    			animation_player.play("Run_Back")
    	
    	move_and_slide()
    10 days later

    I think you can simplify this if you use the transform and basis of the object. the basis will have a vector z that you can always point "forward" and so all you need to do is set the velocity based on that z basis vector before the move and slide.

    # -x is right, +x left, -y forward, +y backward
    var direction : Vector2 =  Input.get_vector("move_forward","move_back","turn_right,"turn_left")
    
    # do movement
    # speed will be 0.0 if direction is not pressed or forward and reverse are pressed at same time
    var speed = direction.y
    if direction.y < 0.0: # forward
      speed =  speed * FORWARD_SPEED 
    else:
      speed = speed * BACKWARD_SPEED 
    velocity = self.global_transform.basis.z * speed
    
    # do rotation
    if direction.y > 0.0:  # if moving backwards
      rotate_y(TURN_SPEED * -direction.x)
    else:
      rotate_y(TURN_SPEED * direction.x)