From a36ae6e38a7db1a6facaeea545a9e9592b2eb72b Mon Sep 17 00:00:00 2001 From: David Briscoe Date: Sat, 14 Oct 2023 23:31:46 -0700 Subject: [PATCH] Rework camera to work with gamepad Fix camera moves 180 degrees every frame when using gamepad. Store a rotation delta and use rotate_toward to move with a max speed instead of a lerp. Has the same general feel with mouse, but now gamepad feels reasonable. --- objects/player.gd | 62 +++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/objects/player.gd b/objects/player.gd index 17af10d..160c870 100644 --- a/objects/player.gd +++ b/objects/player.gd @@ -3,6 +3,7 @@ extends CharacterBody3D @export_subgroup("Properties") @export var movement_speed = 5 @export var jump_strength = 7 +@export var max_look_speed := Vector2.ONE * 5.0 # in radians per second @export_subgroup("Weapons") @export var weapons: Array[Weapon] = [] @@ -10,11 +11,12 @@ extends CharacterBody3D var weapon: Weapon var weapon_index := 0 -var mouse_sensitivity = 700 +var gamepad_sensitivity := 0.05 # max radians we try to move per physics frame +var mouse_sensitivity = 700 # 1/max radians we try to move per mouse input var mouse_captured := true var movement_velocity: Vector3 -var rotation_target: Vector3 +var rotation_delta: Vector2 # x: horizontal, y: vertical var input: Vector3 var input_mouse: Vector2 @@ -47,10 +49,26 @@ signal health_updated func _ready(): Input.mouse_mode = Input.MOUSE_MODE_CAPTURED - + rotation_delta = Vector2.ZERO + initiate_change_weapon(weapon_index) -func _physics_process(delta): + +func angle_difference(from: float, to: float) -> float: + var diff := fmod(to - from, TAU) + return fmod(2.0 * diff, TAU) - diff + + +func rotate_toward(from: float, to: float, delta: float) -> float: + # Not yet in gdscript: https://github.com/godotengine/godot/pull/80225 + var diff := angle_difference(from, to) + var diff_abs := absf(diff) + # When `delta < 0` move no further than to PI radians away from `p_to` (as + # PI is the max possible angle distance). + return from + clamp(delta, diff_abs - PI, diff_abs) * signf(diff) + + +func _physics_process(delta: float): # Handle functions @@ -70,12 +88,19 @@ func _physics_process(delta): move_and_slide() # Rotation - - camera.rotation.z = lerp_angle(camera.rotation.z, -input_mouse.x * 25 * delta, delta * 5) - - camera.rotation.x = lerp_angle(camera.rotation.x, rotation_target.x, delta * 25) - rotation.y = lerp_angle(rotation.y, rotation_target.y, delta * 25) - + + # Roll a bit when turning horizontally. + camera.rotation.z = lerp_angle(camera.rotation.z, -input_mouse.x * 25 * delta, delta * 5) + + var look_amount := max_look_speed * delta + # pitch + var pitch := rotate_toward(camera.rotation.x, camera.rotation.x + rotation_delta.y, look_amount.x) + pitch = clamp(pitch, -TAU * 0.23, TAU * 0.23) + camera.rotation.x = pitch + # yaw + rotation.y = rotate_toward(rotation.y, rotation.y + rotation_delta.x, look_amount.y) + rotation_delta = Vector2.ZERO + container.position = lerp(container.position, container_offset - (applied_velocity / 30), delta * 10) # Movement sound @@ -108,8 +133,8 @@ func _input(event): input_mouse = event.relative / mouse_sensitivity - rotation_target.y -= event.relative.x / mouse_sensitivity - rotation_target.x -= event.relative.y / mouse_sensitivity + rotation_delta.x -= event.relative.x / mouse_sensitivity + rotation_delta.y -= event.relative.y / mouse_sensitivity func handle_controls(_delta): @@ -133,15 +158,10 @@ func handle_controls(_delta): movement_velocity = input.normalized() * movement_speed # Rotation - - var rotation_input := Vector3.ZERO - - rotation_input.y = Input.get_axis("camera_left", "camera_right") - rotation_input.x = Input.get_axis("camera_up", "camera_down") / 2 - - rotation_target -= rotation_input.limit_length(1.0) * 5 - rotation_target.x = clamp(rotation_target.x, deg_to_rad(-90), deg_to_rad(90)) - + + var rotation_input := Input.get_vector("camera_right", "camera_left", "camera_down", "camera_up") + rotation_delta += rotation_input * gamepad_sensitivity + # Shooting action_shoot()