My character's rendering does not match the direction.

Godot Version

4.4.1
I’m a complete beginner and I’ve been stuck on this for a very long time. Please help me. Part of me thinks it’s because my character isn’t facing the direction it’s moving. I’ve tried to fix this, but there are still bugs. This is the best version of my code so far.

extends CharacterBody3D

@export var camera:Camera3D
@onready var animation_tree = $AnimationTree

const SPEED = 5.0
const JUMP_VELOCITY = 4.5
var acceleration := 25.0
var angular_acceleration := 7

func _process(_delta: float) -> void:
	if camera == null:
		return

	var to_camera = (camera.global_transform.origin - global_transform.origin).normalized()

	var fwd := global_transform.basis.z
	var left := global_transform.basis.x

	var dot_fwd = fwd.dot(to_camera)
	var dot_left = left.dot(to_camera)

	var angle = rad_to_deg(atan2(dot_left, dot_fwd))
	if angle < 0:
		angle += 360

	var snapped = int(round(angle / 45.0)) * 45 % 360

	var dir_vector = Vector2.RIGHT.rotated(deg_to_rad(snapped)).normalized()

	update_animation_parameters(dir_vector)

func _physics_process(delta: float) -> void:
	## --- GRAVITY --- ###
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump.
	#if Input.is_action_just_pressed("jump") and is_on_floor():
		#velocity.y = JUMP_VELOCITY
		
	## --- MOVEMENT --- ##
	var input_dir := Input.get_vector("move_left", "move_right", "move_up", "move_down")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	direction = direction.rotated(Vector3.UP, camera.global_rotation.y)
	
	if direction:
		if is_on_floor() and velocity.y <= 0:
			velocity.x = direction.x * SPEED
			velocity.z = direction.z * SPEED
			set_moving(true)
	else:
		velocity.x = move_toward(velocity.x, 0, delta * acceleration)
		velocity.z = move_toward(velocity.z, 0, delta * acceleration)
		set_moving(false)
		
	move_and_slide()

func set_moving(value):
	animation_tree["parameters/conditions/is_walking"] = value
	animation_tree["parameters/conditions/idle"] = not value
	
func update_animation_parameters(input_dir:Vector2):
	if input_dir == Vector2.ZERO:
		return

	animation_tree.set("parameters/idle/blend_position", input_dir)
	animation_tree.set("parameters/run/blend_position", input_dir)

I can’t upload the video, so I’ll just share the link instead.
VIDEO

Is the character a child of the camera? This would cause them to rotate with it and therefore move in the direction it’s facing. I don’t see how the character is rotated directly by code though so I might be misunderstanding the question. In that case, could you explain in more detail what is the desired result?


In my video, the character doesn’t face the correct direction, but the idle animation is displaying correctly. I just want the character to face the direction of the key I’m pressing. Do you understand what I mean?

To turn the character in the direction you’re pressing, I would get the y-rotation of the camera and set the character’s rotation to something relative to that, e.g.

var input_dir := Input.get_vector("move_left", "move_right", "move_up", "move_down")
rotation.y = camera.rotation.y + atan2(input_dir.x, input_dir.y)

I’m sorry if there is some mistake with this, I can’t test it right now, but at least the concept should work.

I don’t know why this happened.
https://share.icloud.com/photos/0ac6mCFyRuAyIaIkIFYJUcQGQ
Player

extends CharacterBody3D

@export var camera:Camera3D
@onready var animation_tree = $AnimationTree

const SPEED = 5.0
const JUMP_VELOCITY = 4.5
var acceleration := 25.0
var angular_acceleration := 7
	
func _process(_delta: float) -> void:
	if camera == null:
		return

	var to_camera = (camera.global_transform.origin - global_transform.origin).normalized()

	var fwd := global_transform.basis.z
	var left := global_transform.basis.x

	var dot_fwd = fwd.dot(to_camera)
	var dot_left = left.dot(to_camera)

	var angle = rad_to_deg(atan2(dot_left, dot_fwd))
	if angle < 0:
		angle += 360

	var snapped_angle = int(round((angle + 90.0) / 45.0)) * 45 % 360
	var dir_vector = Vector2.RIGHT.rotated(deg_to_rad(snapped_angle)).normalized()

	update_animation_parameters(dir_vector)

func _physics_process(delta: float) -> void:
	## --- GRAVITY --- ###
	if not is_on_floor():
		velocity += get_gravity() * delta

	# Handle jump.
	#if Input.is_action_just_pressed("jump") and is_on_floor():
		#velocity.y = JUMP_VELOCITY
		
	## --- MOVEMENT --- ##
	var input_dir := Input.get_vector("move_left", "move_right", "move_up", "move_down")
	var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	direction = direction.rotated(Vector3.UP, camera.global_rotation.y)
	rotation.y = camera.rotation.y + atan2(input_dir.x, input_dir.y)
	
	if direction:
		if is_on_floor() and velocity.y <= 0:
			velocity.x = direction.x * SPEED
			velocity.z = direction.z * SPEED
			set_moving(true)
	else:
		velocity.x = move_toward(velocity.x, 0, delta * acceleration)
		velocity.z = move_toward(velocity.z, 0, delta * acceleration)
		set_moving(false)
		
	move_and_slide()

func set_moving(value):
	animation_tree["parameters/conditions/is_walking"] = value
	animation_tree["parameters/conditions/idle"] = not value
	
func update_animation_parameters(input_dir:Vector2):
	if input_dir == Vector2.ZERO:
		return

	animation_tree.set("parameters/idle/blend_position", input_dir)
	animation_tree.set("parameters/run/blend_position", input_dir)

Hi, what are your other scripts on the camera and on your SpringArmPivot ?

SpringArmPivot

extends Node3D

@export var mouse_sensibility:float = 0.005
@export var pitch_angle_deg:float = -45.0
@export_range(-90.0, 0.0, 0.1, "radians_as_deegrees") var vertical_angle:float = -PI/6

func _ready() -> void:
	Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
	rotation.x = vertical_angle
	
func _unhandled_input(event: InputEvent) -> void:
	if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
		rotation.y -= event.relative.x * mouse_sensibility
		rotation.y = wrapf(rotation.y, 0.0, TAU)
		
	if event.is_action_pressed("toggle_mouse_capture"):
		if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
			Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
		else:
			Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

Camera

extends Camera3D

@export var spring_arm:Node3D
@export var lerp_power:float = 1.0

func _process(delta: float) -> void:
	position = lerp(position, spring_arm.position, delta * lerp_power)

Shouldn’t you be using the SpringArmPivot rotation here instead of the camera ? because the camera is a child of the springarm pivot and it doesn’t seems like it’s rotation.y is changing

I added that after Yesko suggested it. What I’m trying to do is make the character face the direction it’s moving.

https://drive.google.com/file/d/1eUiPMsrk3i8HKSXSVE04bmNNEcWmgour/view?usp=sharing

The square box in front is the character’s forward direction, and the movement is relative to the camera angle like in a typical 3rd person controller. I tried to make it rotate before, but I kept running into weird issues. Can you help me out?

Alright guys, I figured it out — it was my mistake. When I rotated the root node, it made everything rotate with it. So I tried rotating just the sprite instead, and it worked!

var fwd : Vector3 = animated_sprite.global_transform.basis.z
var left : Vector3 = animated_sprite.global_transform.basis.x
var look_position := global_position + Vector3(velocity.x, 0, velocity.z)
animated_sprite.look_at(look_position, Vec