From 5f4a9e409ff71924839981688b8bc0ed685186b9 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Thu, 2 Oct 2025 03:54:19 +0200 Subject: [PATCH] Use `InputEventMouseMotion.screen_relative` where relevant in all demos (#1232) This provides mouse sensitivity that is independent of the viewport size, without needing to query for project settings. This also inverts the mouse motion direction in the 3D navigation demo to better match expectations, and increases mouse sensitivity in the Window Management demo to be closer to other demos. --- 2d/glow/beach_cave.gd | 2 +- 3d/antialiasing/anti_aliasing.gd | 5 ++--- 3d/csg/csg.gd | 5 ++--- 3d/decals/tester.gd | 5 ++--- 3d/global_illumination/camera.gd | 4 ++-- 3d/ik/fps/example_player.gd | 4 ++-- 3d/labels_and_texts/3d_labels_and_texts.gd | 5 ++--- 3d/lights_and_shadows/tester.gd | 5 ++--- 3d/material_testers/tester.gd | 5 ++--- 3d/navigation/navmesh.gd | 2 +- 3d/occlusion_culling_mesh_lod/camera.gd | 4 ++-- 3d/particles/tester.gd | 5 ++--- 3d/physics_tests/utils/camera_orbit.gd | 2 +- 3d/procedural_materials/tester.gd | 5 ++--- 3d/sprites/scripts/3d_sprites.gd | 5 ++--- 3d/volumetric_fog/camera.gd | 4 ++-- 3d/voxel/player/player.gd | 2 +- 3d/waypoints/camera.gd | 4 ++-- misc/large_world_coordinates/controls.gd | 6 ++---- misc/window_management/observer/observer.gd | 9 ++++----- viewport/dynamic_split_screen/camera_controller.gd | 1 - 21 files changed, 38 insertions(+), 51 deletions(-) diff --git a/2d/glow/beach_cave.gd b/2d/glow/beach_cave.gd index 77ecf034..a72ab9e9 100644 --- a/2d/glow/beach_cave.gd +++ b/2d/glow/beach_cave.gd @@ -8,7 +8,7 @@ var glow_map := preload("res://glow_map.webp") func _unhandled_input(event: InputEvent) -> void: if event is InputEventMouseMotion and event.button_mask > 0: - cave.position.x = clampf(cave.position.x + event.relative.x, -CAVE_LIMIT, 0) + cave.position.x = clampf(cave.position.x + event.screen_relative.x, -CAVE_LIMIT, 0) if event.is_action_pressed("toggle_glow_map"): if $WorldEnvironment.environment.glow_map: diff --git a/3d/antialiasing/anti_aliasing.gd b/3d/antialiasing/anti_aliasing.gd index d55cad47..c5d9b96c 100644 --- a/3d/antialiasing/anti_aliasing.gd +++ b/3d/antialiasing/anti_aliasing.gd @@ -8,7 +8,6 @@ var tester_index := 0 var rot_x := -TAU / 16 # This must be kept in sync with RotationX. var rot_y := TAU / 8 # This must be kept in sync with CameraHolder. var camera_distance := 2.0 -var base_height := int(ProjectSettings.get_setting("display/window/size/viewport_height")) @onready var testers: Node3D = $Testers @onready var camera_holder: Node3D = $CameraHolder # Has a position and rotates on Y. @@ -40,8 +39,8 @@ func _unhandled_input(event: InputEvent) -> void: camera_distance = clamp(camera_distance, 1.5, 6) if event is InputEventMouseMotion and event.button_mask & MAIN_BUTTONS: - # Compensate motion speed to be resolution-independent (based on the window height). - var relative_motion: Vector2 = event.relative * DisplayServer.window_get_size().y / base_height + # Use `screen_relative` to make mouse sensitivity independent of viewport resolution. + var relative_motion: Vector2 = event.screen_relative rot_y -= relative_motion.x * ROT_SPEED rot_x -= relative_motion.y * ROT_SPEED rot_x = clamp(rot_x, -1.57, 0) diff --git a/3d/csg/csg.gd b/3d/csg/csg.gd index 914a6dd9..73df8d9f 100644 --- a/3d/csg/csg.gd +++ b/3d/csg/csg.gd @@ -8,7 +8,6 @@ var tester_index := 0 var rot_x := -TAU / 16 # This must be kept in sync with RotationX. var rot_y := TAU / 8 # This must be kept in sync with CameraHolder. var camera_distance := 4.0 -var base_height := int(ProjectSettings.get_setting("display/window/size/viewport_height")) @onready var testers: Node3D = $Testers @onready var camera_holder: Node3D = $CameraHolder # Has a position and rotates on Y. @@ -35,8 +34,8 @@ func _unhandled_input(event: InputEvent) -> void: camera_distance = clamp(camera_distance, 1.5, 6) if event is InputEventMouseMotion and event.button_mask & MAIN_BUTTONS: - # Compensate motion speed to be resolution-independent (based on the window height). - var relative_motion: Vector2 = event.relative * DisplayServer.window_get_size().y / base_height + # Use `screen_relative` to make mouse sensitivity independent of viewport resolution. + var relative_motion: Vector2 = event.screen_relative rot_y -= relative_motion.x * ROT_SPEED rot_x -= relative_motion.y * ROT_SPEED rot_x = clamp(rot_x, -1.57, 0) diff --git a/3d/decals/tester.gd b/3d/decals/tester.gd index 85022b54..37419f39 100644 --- a/3d/decals/tester.gd +++ b/3d/decals/tester.gd @@ -8,7 +8,6 @@ var tester_index := 0 var rot_x := deg_to_rad(-22.5) # This must be kept in sync with RotationX. var rot_y := deg_to_rad(90) # This must be kept in sync with CameraHolder. var zoom := 1.5 -var base_height: = int(ProjectSettings.get_setting("display/window/size/viewport_height")) @onready var testers: Node3D = $Testers @onready var camera_holder: Node3D = $CameraHolder # Has a position and rotates on Y. @@ -49,8 +48,8 @@ func _unhandled_input(event: InputEvent) -> void: zoom = clampf(zoom, 1.5, 4) if event is InputEventMouseMotion and event.button_mask & MAIN_BUTTONS: - # Compensate motion speed to be resolution-independent (based on the window height). - var relative_motion: Vector2 = event.relative * DisplayServer.window_get_size().y / base_height + # Use `screen_relative` to make mouse sensitivity independent of viewport resolution. + var relative_motion: Vector2 = event.screen_relative rot_y -= relative_motion.x * ROT_SPEED rot_x -= relative_motion.y * ROT_SPEED rot_x = clampf(rot_x, deg_to_rad(-90), 0) diff --git a/3d/global_illumination/camera.gd b/3d/global_illumination/camera.gd index f6ad1f4e..259b9f59 100644 --- a/3d/global_illumination/camera.gd +++ b/3d/global_illumination/camera.gd @@ -14,9 +14,9 @@ func _input(event: InputEvent) -> void: # Mouse look (only if the mouse is captured). if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: # Horizontal mouse look. - rot.y -= event.relative.x * MOUSE_SENSITIVITY + rot.y -= event.screen_relative.x * MOUSE_SENSITIVITY # Vertical mouse look. - rot.x = clamp(rot.x - event.relative.y * MOUSE_SENSITIVITY, -1.57, 1.57) + rot.x = clamp(rot.x - event.screen_relative.y * MOUSE_SENSITIVITY, -1.57, 1.57) transform.basis = Basis.from_euler(rot) if event.is_action_pressed("toggle_mouse_capture"): diff --git a/3d/ik/fps/example_player.gd b/3d/ik/fps/example_player.gd index 38cf3886..a9d883e3 100644 --- a/3d/ik/fps/example_player.gd +++ b/3d/ik/fps/example_player.gd @@ -204,8 +204,8 @@ func process_movement(delta): # Mouse based camera movement func _input(event): if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: - rotate_y(deg_to_rad(event.relative.x * MOUSE_SENSITIVITY * -1)) - camera_holder.rotate_x(deg_to_rad(event.relative.y * MOUSE_SENSITIVITY)) + rotate_y(deg_to_rad(event.screen_relative.x * MOUSE_SENSITIVITY * -1)) + camera_holder.rotate_x(deg_to_rad(event.screen_relative.y * MOUSE_SENSITIVITY)) # We need to clamp the camera's rotation so we cannot rotate ourselves upside down var camera_rot = camera_holder.rotation_degrees diff --git a/3d/labels_and_texts/3d_labels_and_texts.gd b/3d/labels_and_texts/3d_labels_and_texts.gd index 78ab269b..fe3bbb30 100644 --- a/3d/labels_and_texts/3d_labels_and_texts.gd +++ b/3d/labels_and_texts/3d_labels_and_texts.gd @@ -8,7 +8,6 @@ var tester_index := 0 var rot_x := -TAU / 16 # This must be kept in sync with RotationX. var rot_y := TAU / 8 # This must be kept in sync with CameraHolder. var camera_distance := 2.0 -var base_height: = int(ProjectSettings.get_setting("display/window/size/viewport_height")) @onready var testers: Node3D = $Testers @onready var camera_holder: Node3D = $CameraHolder # Has a position and rotates on Y. @@ -36,8 +35,8 @@ func _unhandled_input(event: InputEvent) -> void: camera_distance = clamp(camera_distance, 1.5, 6) if event is InputEventMouseMotion and event.button_mask & MAIN_BUTTONS: - # Compensate motion speed to be resolution-independent (based on the window height). - var relative_motion: Vector2 = event.relative * DisplayServer.window_get_size().y / base_height + # Use `screen_relative` to make mouse sensitivity independent of viewport resolution. + var relative_motion: Vector2 = event.screen_relative rot_y -= relative_motion.x * ROT_SPEED rot_x -= relative_motion.y * ROT_SPEED rot_x = clamp(rot_x, -1.57, 0) diff --git a/3d/lights_and_shadows/tester.gd b/3d/lights_and_shadows/tester.gd index 33da164e..8c3956b4 100644 --- a/3d/lights_and_shadows/tester.gd +++ b/3d/lights_and_shadows/tester.gd @@ -8,7 +8,6 @@ var tester_index := 0 var rot_x := deg_to_rad(-22.5) # This must be kept in sync with RotationX. var rot_y := deg_to_rad(90) # This must be kept in sync with CameraHolder. var zoom := 2.5 -var base_height := int(ProjectSettings.get_setting("display/window/size/viewport_height")) @onready var testers: Node3D = $Testers @onready var camera_holder: Node3D = $CameraHolder # Has a position and rotates on Y. @@ -36,8 +35,8 @@ func _unhandled_input(event: InputEvent) -> void: zoom = clamp(zoom, 1.5, 4) if event is InputEventMouseMotion and event.button_mask & MAIN_BUTTONS: - # Compensate motion speed to be resolution-independent (based on the window height). - var relative_motion: Vector2 = event.relative * DisplayServer.window_get_size().y / base_height + # Use `screen_relative` to make mouse sensitivity independent of viewport resolution. + var relative_motion: Vector2 = event.screen_relative rot_y -= relative_motion.x * ROT_SPEED rot_x -= relative_motion.y * ROT_SPEED rot_x = clamp(rot_x, deg_to_rad(-90), 0) diff --git a/3d/material_testers/tester.gd b/3d/material_testers/tester.gd index b7b7581a..7adae5a1 100644 --- a/3d/material_testers/tester.gd +++ b/3d/material_testers/tester.gd @@ -10,7 +10,6 @@ var tester_index := 0 var rot_x := -0.5 # This must be kept in sync with RotationX. var rot_y := -0.5 # This must be kept in sync with CameraHolder. var zoom := 5.0 -var base_height := int(ProjectSettings.get_setting("display/window/size/viewport_height")) var backgrounds: Array[Dictionary] = [ { path = "res://backgrounds/schelde.hdr", name = "Riverside" }, @@ -49,8 +48,8 @@ func _unhandled_input(event: InputEvent) -> void: camera.position.z = zoom if event is InputEventMouseMotion and event.button_mask & MAIN_BUTTONS: - # Compensate motion speed to be resolution-independent (based on the window height). - var relative_motion: Vector2 = event.relative * DisplayServer.window_get_size().y / base_height + # Use `screen_relative` to make mouse sensitivity independent of viewport resolution. + var relative_motion: Vector2 = event.screen_relative rot_y -= relative_motion.x * ROT_SPEED rot_y = clamp(rot_y, -1.95, 1.95) rot_x -= relative_motion.y * ROT_SPEED diff --git a/3d/navigation/navmesh.gd b/3d/navigation/navmesh.gd index 0b293896..954ef926 100644 --- a/3d/navigation/navmesh.gd +++ b/3d/navigation/navmesh.gd @@ -24,5 +24,5 @@ func _unhandled_input(event: InputEvent) -> void: elif event is InputEventMouseMotion: if event.button_mask & (MOUSE_BUTTON_MASK_MIDDLE + MOUSE_BUTTON_MASK_RIGHT): - _cam_rotation += event.relative.x * 0.005 + _cam_rotation -= event.screen_relative.x * 0.005 $CameraBase.set_rotation(Vector3.UP * _cam_rotation) diff --git a/3d/occlusion_culling_mesh_lod/camera.gd b/3d/occlusion_culling_mesh_lod/camera.gd index 65597931..4792cabf 100644 --- a/3d/occlusion_culling_mesh_lod/camera.gd +++ b/3d/occlusion_culling_mesh_lod/camera.gd @@ -14,9 +14,9 @@ func _input(event: InputEvent) -> void: # Mouse look (only if the mouse is captured). if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: # Horizontal mouse look. - rot.y -= event.relative.x * MOUSE_SENSITIVITY + rot.y -= event.screen_relative.x * MOUSE_SENSITIVITY # Vertical mouse look. - rot.x = clamp(rot.x - event.relative.y * MOUSE_SENSITIVITY, -1.57, 1.57) + rot.x = clamp(rot.x - event.screen_relative.y * MOUSE_SENSITIVITY, -1.57, 1.57) transform.basis = Basis.from_euler(rot) if event.is_action_pressed("toggle_mouse_capture"): diff --git a/3d/particles/tester.gd b/3d/particles/tester.gd index 913bdc3b..59bdd160 100644 --- a/3d/particles/tester.gd +++ b/3d/particles/tester.gd @@ -8,7 +8,6 @@ var tester_index := 0 var rot_x := deg_to_rad(-22.5) # This must be kept in sync with RotationX. var rot_y := deg_to_rad(90) # This must be kept in sync with CameraHolder. var zoom := 2.5 -var base_height := int(ProjectSettings.get_setting("display/window/size/viewport_height")) @onready var testers: Node3D = $Testers @onready var camera_holder: Node3D = $CameraHolder # Has a position and rotates on Y. @@ -35,8 +34,8 @@ func _unhandled_input(event: InputEvent) -> void: zoom = clamp(zoom, 1.5, 4) if event is InputEventMouseMotion and event.button_mask & MAIN_BUTTONS: - # Compensate motion speed to be resolution-independent (based on the window height). - var relative_motion: Vector2 = event.relative * DisplayServer.window_get_size().y / base_height + # Use `screen_relative` to make mouse sensitivity independent of viewport resolution. + var relative_motion: Vector2 = event.screen_relative rot_y -= relative_motion.x * ROT_SPEED rot_x -= relative_motion.y * ROT_SPEED rot_x = clamp(rot_x, deg_to_rad(-90), 0) diff --git a/3d/physics_tests/utils/camera_orbit.gd b/3d/physics_tests/utils/camera_orbit.gd index 899504eb..3c81840d 100644 --- a/3d/physics_tests/utils/camera_orbit.gd +++ b/3d/physics_tests/utils/camera_orbit.gd @@ -20,7 +20,7 @@ func _unhandled_input(event: InputEvent) -> void: return if event is InputEventMouseMotion: - var rotation_delta: float = event.relative.x + var rotation_delta: float = event.screen_relative.x _rotation_pivot.rotate(Vector3.UP, -rotation_delta * ROTATION_COEFF) diff --git a/3d/procedural_materials/tester.gd b/3d/procedural_materials/tester.gd index 22107740..1191c173 100644 --- a/3d/procedural_materials/tester.gd +++ b/3d/procedural_materials/tester.gd @@ -8,7 +8,6 @@ var tester_index := 0 var rot_x := deg_to_rad(-22.5) # This must be kept in sync with RotationX. var rot_y := deg_to_rad(90) # This must be kept in sync with CameraHolder. var zoom := 2.5 -var base_height := int(ProjectSettings.get_setting("display/window/size/viewport_height")) @onready var testers: Node3D = $Testers @onready var camera_holder: Node3D = $CameraHolder # Has a position and rotates on Y. @@ -35,8 +34,8 @@ func _unhandled_input(event: InputEvent) -> void: zoom = clampf(zoom, 1.5, 4) if event is InputEventMouseMotion and event.button_mask & MAIN_BUTTONS: - # Compensate motion speed to be resolution-independent (based on the window height). - var relative_motion: Vector2 = event.relative * DisplayServer.window_get_size().y / base_height + # Use `screen_relative` to make mouse sensitivity independent of viewport resolution. + var relative_motion: Vector2 = event.screen_relative rot_y -= relative_motion.x * ROT_SPEED rot_x -= relative_motion.y * ROT_SPEED rot_x = clampf(rot_x, deg_to_rad(-90), 0) diff --git a/3d/sprites/scripts/3d_sprites.gd b/3d/sprites/scripts/3d_sprites.gd index b8582a67..f2047b5b 100644 --- a/3d/sprites/scripts/3d_sprites.gd +++ b/3d/sprites/scripts/3d_sprites.gd @@ -8,7 +8,6 @@ var tester_index := 0 var rot_x := -TAU / 16 # This must be kept in sync with RotationX. var rot_y := TAU / 6 # This must be kept in sync with CameraHolder. var camera_distance := 3.4 -var base_height := int(ProjectSettings.get_setting("display/window/size/viewport_height")) @onready var testers: Node3D = $Testers @onready var camera_holder: Node3D = $CameraHolder # Has a position and rotates on Y. @@ -36,8 +35,8 @@ func _unhandled_input(event: InputEvent) -> void: camera_distance = clamp(camera_distance, 1.5, 6) if event is InputEventMouseMotion and event.button_mask & MAIN_BUTTONS: - # Compensate motion speed to be resolution-independent (based on the window height). - var relative_motion: Vector2 = event.relative * DisplayServer.window_get_size().y / base_height + # Use `screen_relative` to make mouse sensitivity independent of viewport resolution. + var relative_motion: Vector2 = event.screen_relative rot_y -= relative_motion.x * ROT_SPEED rot_x -= relative_motion.y * ROT_SPEED rot_x = clamp(rot_x, -1.57, 0) diff --git a/3d/volumetric_fog/camera.gd b/3d/volumetric_fog/camera.gd index 73de870f..9787a1b0 100644 --- a/3d/volumetric_fog/camera.gd +++ b/3d/volumetric_fog/camera.gd @@ -36,9 +36,9 @@ func _input(event: InputEvent) -> void: # Mouse look (only if the mouse is captured). if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: # Horizontal mouse look. - rot.y -= event.relative.x * MOUSE_SENSITIVITY + rot.y -= event.screen_relative.x * MOUSE_SENSITIVITY # Vertical mouse look. - rot.x = clamp(rot.x - event.relative.y * MOUSE_SENSITIVITY, -1.57, 1.57) + rot.x = clamp(rot.x - event.screen_relative.y * MOUSE_SENSITIVITY, -1.57, 1.57) transform.basis = Basis.from_euler(rot) if event.is_action_pressed("toggle_mouse_capture"): diff --git a/3d/voxel/player/player.gd b/3d/voxel/player/player.gd index bb5679de..69065a20 100644 --- a/3d/voxel/player/player.gd +++ b/3d/voxel/player/player.gd @@ -112,7 +112,7 @@ func _physics_process(delta: float) -> void: func _input(event: InputEvent) -> void: if event is InputEventMouseMotion: if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: - _mouse_motion += event.relative + _mouse_motion += event.screen_relative func chunk_pos() -> Vector3i: diff --git a/3d/waypoints/camera.gd b/3d/waypoints/camera.gd index 4c4d5d59..5ab4646b 100644 --- a/3d/waypoints/camera.gd +++ b/3d/waypoints/camera.gd @@ -14,9 +14,9 @@ func _input(event: InputEvent) -> void: # Mouse look (only if the mouse is captured). if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: # Horizontal mouse look. - rot.y -= event.relative.x * MOUSE_SENSITIVITY + rot.y -= event.screen_relative.x * MOUSE_SENSITIVITY # Vertical mouse look. - rot.x = clamp(rot.x - event.relative.y * MOUSE_SENSITIVITY, -1.57, 1.57) + rot.x = clamp(rot.x - event.screen_relative.y * MOUSE_SENSITIVITY, -1.57, 1.57) transform.basis = Basis.from_euler(rot) if event.is_action_pressed(&"toggle_mouse_capture"): diff --git a/misc/large_world_coordinates/controls.gd b/misc/large_world_coordinates/controls.gd index 0956ee47..6f18fc12 100644 --- a/misc/large_world_coordinates/controls.gd +++ b/misc/large_world_coordinates/controls.gd @@ -11,8 +11,6 @@ const MAIN_BUTTONS = MOUSE_BUTTON_MASK_LEFT | MOUSE_BUTTON_MASK_MIDDLE | MOUSE_B @export var rigid_body: RigidBody3D @onready var zoom := camera.position.z -var base_height: int = ProjectSettings.get_setting("display/window/size/viewport_height") - @onready var rot_x := rotation_x.rotation.x @onready var rot_y := camera_holder.rotation.y @@ -42,8 +40,8 @@ func _input(event: InputEvent) -> void: camera.position.z = zoom if event is InputEventMouseMotion and event.button_mask & MAIN_BUTTONS: - # Compensate motion speed to be resolution-independent (based on the window height). - var relative_motion: Vector2 = event.relative * DisplayServer.window_get_size().y / base_height + # Use `screen_relative` to make mouse sensitivity independent of viewport resolution. + var relative_motion: Vector2 = event.screen_relative rot_y -= relative_motion.x * ROT_SPEED rot_x -= relative_motion.y * ROT_SPEED rot_x = clampf(rot_x, -1.4, 0.16) diff --git a/misc/window_management/observer/observer.gd b/misc/window_management/observer/observer.gd index 548d428b..e7b0155c 100644 --- a/misc/window_management/observer/observer.gd +++ b/misc/window_management/observer/observer.gd @@ -5,11 +5,11 @@ enum State { GRAB, } +const MOUSE_SENSITIVITY = 3.0 + var r_pos := Vector2() var state := State.MENU -var initial_viewport_height := int(ProjectSettings.get_setting("display/window/size/viewport_height")) - @onready var camera: Camera3D = $Camera3D func _process(delta: float) -> void: @@ -32,9 +32,8 @@ func _process(delta: float) -> void: func _input(event: InputEvent) -> void: if event is InputEventMouseMotion: - # Scale mouse sensitivity according to resolution, so that effective mouse sensitivity - # doesn't change depending on the viewport size. - r_pos = -event.relative * (get_viewport().size.y / initial_viewport_height) + # Use `screen_relative` to make sensitivity independent of the viewport resolution. + r_pos = -event.screen_relative * MOUSE_SENSITIVITY if event.is_action("ui_cancel") and event.is_pressed() and not event.is_echo(): if state == State.GRAB: diff --git a/viewport/dynamic_split_screen/camera_controller.gd b/viewport/dynamic_split_screen/camera_controller.gd index a946086d..cc85d693 100644 --- a/viewport/dynamic_split_screen/camera_controller.gd +++ b/viewport/dynamic_split_screen/camera_controller.gd @@ -31,7 +31,6 @@ extends Node3D @onready var camera1: Camera3D = viewport1.get_node(^"Camera1") @onready var camera2: Camera3D = viewport2.get_node(^"Camera2") -var viewport_base_height := int(ProjectSettings.get_setting("display/window/size/viewport_height")) func _ready() -> void: _on_size_changed()