How to rotate objact around local forward axis regardless of global rotation

Godot Version

godot 4

Question

how do you rotate an object around it’s local forward axis regardless of it’s global rotation?

I’m working on a lean mechanic for my fps game, and currently the lean code rotates the player’s character body in the same global direction(east/west) regardless of where you’re facing, so if you’re facing west, it rotates you forward and back instead of left and right.

here’s the code currently:

if Input.is_action_pressed("leanLeft"):
	leanDir = 1
	rotation.z = lerp_angle(rotation.z, leanDir, delta * 0.1)
elif Input.is_action_pressed("leanRight"):
	leanDir = -1
	rotation.z = lerp_angle(rotation.z, leanDir, delta * 0.1)
else:
	leanDir = 0
	rotation.z = lerp(rotation.z, 0.0, delta * 5)
	
rotation.z = clamp(rotation.z, deg_to_rad(-30), deg_to_rad(30))
camera.rotation.x = clamp(camera.rotation.x, deg_to_rad(-89), deg_to_rad(90))
	
	

Node3D has a rotate_object_local that I’ve used to make a relative rotation.

it seems to still be rotating around the global z instead of the local

You need to multiply your rotation vector by the basis to have any meaningful value. Ideally, you also want to separate your model and/or camera Nodes with a “pivot” Node3D that you will be rotating instead of rotating directly the root Node, otherwise it gets messy to synchronize all the rotations together, especially when you want to tween/lerp the values.

The easiest I could come up is this little demo.

Here’s a scene structure:

Here’s the code attached to the Player node:

extends CharacterBody3D


@export var camera_pivot: Node3D
var speed: float = 5.0
var lean_angle: float = PI / 8
var tween_duration: float = 0.2


func _unhandled_input(event: InputEvent) -> void:
	# Player left/right rotation with mouse motion
	if event is InputEventMouseMotion:
		rotate(Vector3.UP, -event.relative.x * 0.01)
	
	# Lean right
	if Input.is_action_just_pressed("ui_right"):
		var tween: Tween = create_tween()
		tween.tween_method(func(angle: float): camera_pivot.rotation = camera_pivot.basis * Vector3.BACK * angle, 0.0, -lean_angle, tween_duration)
	elif Input.is_action_just_released("ui_right"):
		var tween: Tween = create_tween()
		tween.tween_method(func(angle: float): camera_pivot.rotation = camera_pivot.basis * Vector3.BACK * angle, -lean_angle, 0.0, tween_duration)
	
	# Lean left
	if Input.is_action_just_pressed("ui_left"):
		var tween: Tween = create_tween()
		tween.tween_method(func(angle: float): camera_pivot.rotation = camera_pivot.basis * Vector3.BACK * angle, 0.0, lean_angle, tween_duration)
	elif Input.is_action_just_released("ui_left"):
		var tween: Tween = create_tween()
		tween.tween_method(func(angle: float): camera_pivot.rotation = camera_pivot.basis * Vector3.BACK * angle, lean_angle, 0.0, tween_duration)


func _physics_process(delta: float) -> void:
	# Player forward movement
	velocity = Vector3.ZERO
	if Input.is_action_pressed("ui_up"):
		velocity = basis * Vector3.FORWARD * speed
	move_and_slide()

You’d need to adjust it to fit your project, but hopefully this will nudge you in the right direction.

1 Like

that seems like a really great way to do it. for some reason, even multiplying by the basis still isn’t working. I just tried printing the rotation and the basis of the object I’m rotating, and even though it is clearly and visibly rotating, the printed rotation, basis, and global rotation aren’t changing, and it only prints 0,0,0. I don’t understand why

@bannanaking3 Show your code after adjustments please.

no need! turns out the issue is my lack of comments on my own code! apparently at one point I had added arrow key rotation functionality on top of mouse rotation, (I don’t remember why), and the mouse rotation had no control of the parent’s y rotation value. I really appreciate your help, your solution is the strongest I’ve seen out of any tutorials or anything.

1 Like

organization bites me again, who knew