Godot version 4.3
Hi,
I’m currently teaching myself Quaternions and to better grasp the concept I’ve decided to make a FPS camera ( camera orientation with the mouse and movement with WASD).
So far, everything works with a single script using GDscript, walking follows camera orientation as intended.
The only problem I’m facing so far is that when I look up or down the movement slows down gradually and stops when watching the ground or the sky.
Here’s the code :
extends Camera3D
@export_range(0,100) var look_speed : float = 50
@export_range(0,100) var look_smooth : float = 50
@export_range(0,100) var move_speed : float = 50
var pos
var move_scale = 0.1
var mouse_locked = true
var look_scale = 0.00001
var threshold = 0.00001
var target_basis = Basis.IDENTITY
var smooth_scale = 0.001
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pos = get_node("/root/Test2/Player/Camera_dummy") # Here for initial camera placement
self.position = pos.global_position
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
var move_dir = check_buttons()
transform.basis = transform.basis.slerp(target_basis, look_smooth * smooth_scale)
move_dir = transform.basis * move_dir
var next_pos = self.global_position + (move_dir * move_speed * move_scale * delta)
self.global_position = Vector3(next_pos.x, self.global_position.y, next_pos.z)
func _input(event: InputEvent) -> void:
if event is InputEventMouseMotion and mouse_locked:
var mouse_delta = -event.relative * look_speed * look_scale
var delta_y_max = (-target_basis.z).angle_to(Vector3.UP * sign(mouse_delta.y))
mouse_delta.y = clamp(abs(mouse_delta.y), 0.0, delta_y_max - threshold) * sign(mouse_delta.y)
var horz_quat = Quaternion(Vector3.UP * target_basis, mouse_delta.x)
var vert_quat = Quaternion(Vector3.RIGHT, mouse_delta.y)
target_basis *= Basis(horz_quat * vert_quat)
target_basis = target_basis.orthonormalized()
func check_buttons():
var dir = Vector3.ZERO
if Input.is_action_pressed("forward"):
dir += Vector3.FORWARD
if Input.is_action_pressed("back"):
dir += Vector3.BACK
if Input.is_action_pressed("left"):
dir += Vector3.LEFT
if Input.is_action_pressed("right"):
dir += Vector3.RIGHT
if not dir.is_equal_approx(Vector3.ZERO):
dir = dir.normalized()
return dir
So far, nothing surprising since my transform.basis has been rotated and the Z axis is not aligned with the world anymore hence the length of Z in my “move_dir” and the speed diminution.
What I’ve tried :
-
Making the camera a children of a CharacterBody3D and applying camera rotation on the Y axis to it. It works but cornering becomes choppy since, I suppose, CharacterBody and Camera rotation are fighting each other.
-
Normalizing “move_dir” after multiplying it with transform.basis. No luck. Printing the value in the console shows values different from 0 and 1. I think it’s because the initial values are decimal below 1.
-
Trying to force normalize on “move_dir” with a function that override the values if it’s below or over 0. Funky movement everytime !
I think I’m missing a good chunk of knowledge on how to handle transform properly so any help would be much appreciated.
Thanks a bunch.