Godot Version
4.5.1.stable
Question
Hi, I’m trying to make a game in a zero gravity 3d environment, where the character can rotate freely in all directions, (think Outer Wilds, Hard Space: Ship Breaker, Space Engineers, etc); but I’m having trouble with the camera controls. They don’t seem to work properly relative to the current/actual orientation of the camera.
Is anyone able to help me fix this code, or guide me towards the right functions (like maybe I need to try to figure out quaternions? etc.)
func _unhandled_input(event: InputEvent) -> void:
# Mouse capturing
if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
capture_mouse()
if Input.is_key_pressed(KEY_ESCAPE):
release_mouse()
if event is InputEventMouseMotion:
rot_x -= event.screen_relative.x * look_speed
rot_y -= event.screen_relative.y * look_speed
if Input.is_action_pressed("move_tilt_left"):
rot_z += tilt_speed
if Input.is_action_pressed("move_tilt_right"):
rot_z -= tilt_speed
transform.basis = Basis()
rotate_object_local(Vector3(0,1,0), rot_x) # first rotate in Y
rotate_object_local(Vector3(1,0,0), rot_y) # then rotate in X
rotate_object_local(Vector3(0,0,1), rot_z) #rotate in Z
Is this first person or third person? Which node is this script attached to? Post your scene structure.
This is first person. This is my node structure. This script is attached to the player, and the camera is attached direction on top of the player.
I’ve been trying to mess around with quaternions since it seems to be the elegant (but difficult) solution. I’m not sure if using three nodes with each controlling one axis would solve gimbal lock problems.
Don’t reset the basis if you want to rotate around current axes. Just do local delta rotations that correspond to input.
func _unhandled_input(event: InputEvent) -> void:
## Mouse capturing
if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
capture_mouse()
if Input.is_key_pressed(KEY_ESCAPE):
release_mouse()
##capturing inputs
if event is InputEventMouseMotion:
rotate_object_local(basis.y,-event.relative.x * look_speed,)
rotate_object_local(basis.x,-event.relative.y * look_speed,)
if Input.is_action_pressed("move_tilt_left"):
rotate_object_local(basis.z,tilt_speed)
if Input.is_action_pressed("move_tilt_right"):
rotate_object_local(-basis.z,tilt_speed)
this is my code now before the earlier reply.
is this what you mean by local delta rotations? or I am I misunderstanding them vs rotate_object_local
Almost. If you use rotate_object_local() the axes need to be like in version you first posted, not basis axes.
bless your soul
func _unhandled_input(event: InputEvent) -> void:
## Mouse capturing
if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
capture_mouse()
if Input.is_key_pressed(KEY_ESCAPE):
release_mouse()
##capturing inputs
if event is InputEventMouseMotion:
rotate_object_local(Vector3(0,1,0),-event.relative.x * look_speed,)
rotate_object_local(Vector3(1,0,0),-event.relative.y * look_speed,)
if Input.is_action_pressed("move_tilt_left"):
rotate_object_local(Vector3(0,0,1),tilt_speed)
if Input.is_action_pressed("move_tilt_right"):
rotate_object_local(Vector3(0,0,1),-tilt_speed)
basis = basis.orthonormalized()
Here’s the code for anyone who stumbles on this in the future.
1 Like
Since you’re doing a lot of cumulative rotations, it’d be good idea to orthonormalize the basis to prevent precision errors buildup. As a last line add:
basis = basis.orthonormalized()
1 Like