Repository: godotengine/godot (language: cpp)

Issue:
Sensitivity settings don't work in embedded game window
### Tested versions

v4.4.stable.official [4c311cbee]

### System information

Godot v4.4.stable - Linux Mint 22.1 (Xia) on X11 - X11 display driver, Multi-window, 2 monitors - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 960 (nvidia; 550.120) - AMD FX(tm)-6300 Six-Core Processor (6 threads)

### Issue description

The embedded game window doesn't listen to the sensitivity settings in Editor Settings > Editors > 3D. I think it just uses the default value for every option.

https://github.com/user-attachments/assets/b76f5570-d0c7-45b1-8d50-14649f853cf1

### Steps to reproduce

1. Change one of the sensitivity settings in Editor Settings > Editors > 3D such as "Orbit Sensitivity" or "Freelook Sensitivity".
2. Play the game, in embedded game window click the camera icon and 3D. The sensitivity hasn't changed.

### Minimal reproduction project (MRP)

N/A


Discussion / hints:
cc @YeldhamDev 

Fix patch:
diff --git a/editor/plugins/game_view_plugin.cpp b/editor/plugins/game_view_plugin.cpp
index bc3b4ceeb2b7..fa9dd87bc491 100644
--- a/editor/plugins/game_view_plugin.cpp
+++ b/editor/plugins/game_view_plugin.cpp
@@ -65,7 +65,18 @@ void GameViewDebugger::_session_started(Ref<EditorDebuggerSession> p_session) {
 	settings["editors/panning/warped_mouse_panning"] = EDITOR_GET("editors/panning/warped_mouse_panning");
 	settings["editors/panning/2d_editor_pan_speed"] = EDITOR_GET("editors/panning/2d_editor_pan_speed");
 	settings["canvas_item_editor/pan_view"] = DebuggerMarshalls::serialize_key_shortcut(ED_GET_SHORTCUT("canvas_item_editor/pan_view"));
+#ifndef _3D_DISABLED
+	settings["editors/3d/default_fov"] = EDITOR_GET("editors/3d/default_fov");
+	settings["editors/3d/default_z_near"] = EDITOR_GET("editors/3d/default_z_near");
+	settings["editors/3d/default_z_far"] = EDITOR_GET("editors/3d/default_z_far");
+	settings["editors/3d/navigation/invert_x_axis"] = EDITOR_GET("editors/3d/navigation/invert_x_axis");
+	settings["editors/3d/navigation/invert_y_axis"] = EDITOR_GET("editors/3d/navigation/invert_y_axis");
+	settings["editors/3d/navigation/warped_mouse_panning"] = EDITOR_GET("editors/3d/navigation/warped_mouse_panning");
 	settings["editors/3d/freelook/freelook_base_speed"] = EDITOR_GET("editors/3d/freelook/freelook_base_speed");
+	settings["editors/3d/freelook/freelook_sensitivity"] = EDITOR_GET("editors/3d/freelook/freelook_sensitivity");
+	settings["editors/3d/navigation_feel/orbit_sensitivity"] = EDITOR_GET("editors/3d/navigation_feel/orbit_sensitivity");
+	settings["editors/3d/navigation_feel/translation_sensitivity"] = EDITOR_GET("editors/3d/navigation_feel/translation_sensitivity");
+#endif // _3D_DISABLED
 	setup_data.append(settings);
 	p_session->send_message("scene:runtime_node_select_setup", setup_data);
 
diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp
index c620c5fd5c80..5661268f04c3 100644
--- a/scene/debugger/scene_debugger.cpp
+++ b/scene/debugger/scene_debugger.cpp
@@ -1261,7 +1261,18 @@ void RuntimeNodeSelect::_setup(const Dictionary &p_settings) {
 #ifndef _3D_DISABLED
 	cursor = Cursor();
 
-	freelook_speed = p_settings.get("editors/3d/freelook/freelook_base_speed", FREELOOK_BASE_SPEED);
+	camera_fov = p_settings.get("editors/3d/default_fov", 70);
+	camera_znear = p_settings.get("editors/3d/default_z_near", 0.05);
+	camera_zfar = p_settings.get("editors/3d/default_z_far", 4'000);
+
+	invert_x_axis = p_settings.get("editors/3d/navigation/invert_x_axis", false);
+	invert_y_axis = p_settings.get("editors/3d/navigation/invert_y_axis", false);
+	warped_mouse_panning_3d = p_settings.get("editors/3d/navigation/warped_mouse_panning", true);
+
+	freelook_base_speed = p_settings.get("editors/3d/freelook/freelook_base_speed", 5);
+	freelook_sensitivity = Math::deg_to_rad((real_t)p_settings.get("editors/3d/freelook/freelook_sensitivity", 0.25));
+	orbit_sensitivity = Math::deg_to_rad((real_t)p_settings.get("editors/3d/navigation_feel/orbit_sensitivity", 0.004));
+	translation_sensitivity = p_settings.get("editors/3d/navigation_feel/translation_sensitivity", 1);
 
 	/// 3D Selection Box Generation
 	// Copied from the Node3DEditor implementation.
@@ -1342,7 +1353,7 @@ void RuntimeNodeSelect::_set_camera_override_enabled(bool p_enabled) {
 		_update_view_2d();
 
 		SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform());
-		SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(CAMERA_BASE_FOV * cursor.fov_scale, CAMERA_ZNEAR, CAMERA_ZFAR);
+		SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(camera_fov * cursor.fov_scale, camera_znear, camera_zfar);
 	}
 #endif // _3D_DISABLED
 }
@@ -1445,7 +1456,7 @@ void RuntimeNodeSelect::_process_frame() {
 			direction -= up;
 		}
 
-		real_t speed = freelook_speed;
+		real_t speed = freelook_base_speed;
 		if (input->is_physical_key_pressed(Key::SHIFT)) {
 			speed *= 3.0;
 		}
@@ -2011,19 +2022,19 @@ bool RuntimeNodeSelect::_handle_3d_input(const Ref<InputEvent> &p_event) {
 			switch (k->get_physical_keycode()) {
 				case Key::EQUAL: {
 					cursor.fov_scale = CLAMP(cursor.fov_scale - 0.05, CAMERA_MIN_FOV_SCALE, CAMERA_MAX_FOV_SCALE);
-					SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(CAMERA_BASE_FOV * cursor.fov_scale, CAMERA_ZNEAR, CAMERA_ZFAR);
+					SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(camera_fov * cursor.fov_scale, camera_znear, camera_zfar);
 
 					return true;
 				} break;
 				case Key::MINUS: {
 					cursor.fov_scale = CLAMP(cursor.fov_scale + 0.05, CAMERA_MIN_FOV_SCALE, CAMERA_MAX_FOV_SCALE);
-					SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(CAMERA_BASE_FOV * cursor.fov_scale, CAMERA_ZNEAR, CAMERA_ZFAR);
+					SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(camera_fov * cursor.fov_scale, camera_znear, camera_zfar);
 
 					return true;
 				} break;
 				case Key::KEY_0: {
 					cursor.fov_scale = 1;
-					SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(CAMERA_BASE_FOV, CAMERA_ZNEAR, CAMERA_ZFAR);
+					SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(camera_fov, camera_znear, camera_zfar);
 
 					return true;
 				} break;
@@ -2063,33 +2074,37 @@ void RuntimeNodeSelect::_set_camera_freelook_enabled(bool p_enabled) {
 }
 
 void RuntimeNodeSelect::_cursor_scale_distance(real_t p_scale) {
-	real_t min_distance = MAX(CAMERA_ZNEAR * 4, VIEW_3D_MIN_ZOOM);
-	real_t max_distance = MIN(CAMERA_ZFAR / 4, VIEW_3D_MAX_ZOOM);
+	real_t min_distance = MAX(camera_znear * 4, VIEW_3D_MIN_ZOOM);
+	real_t max_distance = MIN(camera_zfar / 4, VIEW_3D_MAX_ZOOM);
 	cursor.distance = CLAMP(cursor.distance * p_scale, min_distance, max_distance);
 
 	SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform());
 }
 
 void RuntimeNodeSelect::_scale_freelook_speed(real_t p_scale) {
-	real_t min_speed = MAX(CAMERA_ZNEAR * 4, VIEW_3D_MIN_ZOOM);
-	real_t max_speed = MIN(CAMERA_ZFAR / 4, VIEW_3D_MAX_ZOOM);
+	real_t min_speed = MAX(camera_znear * 4, VIEW_3D_MIN_ZOOM);
+	real_t max_speed = MIN(camera_zfar / 4, VIEW_3D_MAX_ZOOM);
 	if (unlikely(min_speed > max_speed)) {
-		freelook_speed = (min_speed + max_speed) / 2;
+		freelook_base_speed = (min_speed + max_speed) / 2;
 	} else {
-		freelook_speed = CLAMP(freelook_speed * p_scale, min_speed, max_speed);
+		freelook_base_speed = CLAMP(freelook_base_speed * p_scale, min_speed, max_speed);
 	}
 }
 
 void RuntimeNodeSelect::_cursor_look(Ref<InputEventWithModifiers> p_event) {
 	Window *root = SceneTree::get_singleton()->get_root();
-	const Vector2 relative = Input::get_singleton()->warp_mouse_motion(p_event, Rect2(Vector2(), root->get_size()));
+	const Vector2 relative = _get_warped_mouse_motion(p_event, Rect2(Vector2(), root->get_size()));
 	const Transform3D prev_camera_transform = _get_cursor_transform();
 
-	cursor.x_rot += relative.y * RADS_PER_PIXEL;
+	if (invert_y_axis) {
+		cursor.x_rot -= relative.y * freelook_sensitivity;
+	} else {
+		cursor.x_rot += relative.y * freelook_sensitivity;
+	}
 	// Clamp the Y rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented.
 	cursor.x_rot = CLAMP(cursor.x_rot, -1.57, 1.57);
 
-	cursor.y_rot += relative.x * RADS_PER_PIXEL;
+	cursor.y_rot += relative.x * freelook_sensitivity;
 
 	// Look is like the opposite of Orbit: the focus point rotates around the camera.
 	Transform3D camera_transform = _get_cursor_transform();
@@ -2104,8 +2119,8 @@ void RuntimeNodeSelect::_cursor_look(Ref<InputEventWithModifiers> p_event) {
 void RuntimeNodeSelect::_cursor_pan(Ref<InputEventWithModifiers> p_event) {
 	Window *root = SceneTree::get_singleton()->get_root();
 	// Reduce all sides of the area by 1, so warping works when windows are maximized/fullscreen.
-	const Vector2 relative = Input::get_singleton()->warp_mouse_motion(p_event, Rect2(Vector2(1, 1), root->get_size() - Vector2(2, 2)));
-	const real_t pan_speed = 1 / 150.0;
+	const Vector2 relative = _get_warped_mouse_motion(p_event, Rect2(Vector2(1, 1), root->get_size() - Vector2(2, 2)));
+	const real_t pan_speed = translation_sensitivity / 150.0;
 
 	Transform3D camera_transform;
 	camera_transform.translate_local(cursor.pos);
@@ -2123,17 +2138,35 @@ void RuntimeNodeSelect::_cursor_pan(Ref<InputEventWithModifiers> p_event) {
 void RuntimeNodeSelect::_cursor_orbit(Ref<InputEventWithModifiers> p_event) {
 	Window *root = SceneTree::get_singleton()->get_root();
 	// Reduce all sides of the area by 1, so warping works when windows are maximized/fullscreen.
-	const Vector2 relative = Input::get_singleton()->warp_mouse_motion(p_event, Rect2(Vector2(1, 1), root->get_size() - Vector2(2, 2)));
+	const Vector2 relative = _get_warped_mouse_motion(p_event, Rect2(Vector2(1, 1), root->get_size() - Vector2(2, 2)));
 
-	cursor.x_rot += relative.y * RADS_PER_PIXEL;
+	if (invert_y_axis) {
+		cursor.x_rot -= relative.y * orbit_sensitivity;
+	} else {
+		cursor.x_rot += relative.y * orbit_sensitivity;
+	}
 	// Clamp the Y rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented.
 	cursor.x_rot = CLAMP(cursor.x_rot, -1.57, 1.57);
 
-	cursor.y_rot += relative.x * RADS_PER_PIXEL;
+	if (invert_x_axis) {
+		cursor.y_rot -= relative.x * orbit_sensitivity;
+	} else {
+		cursor.y_rot += relative.x * orbit_sensitivity;
+	}
 
 	SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform());
 }
 
+Point2 RuntimeNodeSelect::_get_warped_mouse_motion(const Ref<InputEventMouseMotion> &p_event, Rect2 p_area) const {
+	ERR_FAIL_COND_V(p_event.is_null(), Point2());
+
+	if (warped_mouse_panning_3d) {
+		return Input::get_singleton()->warp_mouse_motion(p_event, p_area);
+	}
+
+	return p_event->get_relative();
+}
+
 Transform3D RuntimeNodeSelect::_get_cursor_transform() {
 	Transform3D camera_transform;
 	camera_transform.translate_local(cursor.pos);
@@ -2158,13 +2191,13 @@ void RuntimeNodeSelect::_reset_camera_3d() {
 		cursor.x_rot = -camera->get_global_rotation().x;
 		cursor.y_rot = -camera->get_global_rotation().y;
 
-		cursor.fov_scale = CLAMP(camera->get_fov() / CAMERA_BASE_FOV, CAMERA_MIN_FOV_SCALE, CAMERA_MAX_FOV_SCALE);
+		cursor.fov_scale = CLAMP(camera->get_fov() / camera_fov, CAMERA_MIN_FOV_SCALE, CAMERA_MAX_FOV_SCALE);
 	} else {
 		cursor.fov_scale = 1.0;
 	}
 
 	SceneTree::get_singleton()->get_root()->set_camera_3d_override_transform(_get_cursor_transform());
-	SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(CAMERA_BASE_FOV * cursor.fov_scale, CAMERA_ZNEAR, CAMERA_ZFAR);
+	SceneTree::get_singleton()->get_root()->set_camera_3d_override_perspective(camera_fov * cursor.fov_scale, camera_znear, camera_zfar);
 }
 #endif // _3D_DISABLED
 #endif
diff --git a/scene/debugger/scene_debugger.h b/scene/debugger/scene_debugger.h
index 5a56ea9d99b7..b0a365fddfcb 100644
--- a/scene/debugger/scene_debugger.h
+++ b/scene/debugger/scene_debugger.h
@@ -242,20 +242,26 @@ class RuntimeNodeSelect : public Object {
 	const double VIEW_3D_MAX_ZOOM = 1'000'000'000'000;
 #else
 	const float VIEW_3D_MAX_ZOOM = 10'000;
-#endif
-	const float CAMERA_ZNEAR = 0.05;
-	const float CAMERA_ZFAR = 4'000;
+#endif // REAL_T_IS_DOUBLE
 
-	const float CAMERA_BASE_FOV = 75;
 	const float CAMERA_MIN_FOV_SCALE = 0.1;
 	const float CAMERA_MAX_FOV_SCALE = 2.5;
 
-	const float FREELOOK_BASE_SPEED = 4;
-	const float RADS_PER_PIXEL = 0.004;
-
 	bool camera_first_override = true;
 	bool camera_freelook = false;
-	real_t freelook_speed = FREELOOK_BASE_SPEED;
+
+	real_t camera_fov = 0;
+	real_t camera_znear = 0;
+	real_t camera_zfar = 0;
+
+	bool invert_x_axis = false;
+	bool invert_y_axis = false;
+	bool warped_mouse_panning_3d = false;
+
+	real_t freelook_base_speed = 0;
+	real_t freelook_sensitivity = 0;
+	real_t orbit_sensitivity = 0;
+	real_t translation_sensitivity = 0;
 
 	Vector2 previous_mouse_position;
 
@@ -267,7 +273,7 @@ class RuntimeNodeSelect : public Object {
 	RID sbox_3d_instance_xray_ofs;
 	Transform3D sbox_3d_xform;
 	AABB sbox_3d_bounds;
-#endif
+#endif // _3D_DISABLED
 
 	Point2 selection_position = Point2(INFINITY, INFINITY);
 	bool list_shortcut_pressed = false;
@@ -314,6 +320,7 @@ class RuntimeNodeSelect : public Object {
 	void _cursor_look(Ref<InputEventWithModifiers> p_event);
 	void _cursor_pan(Ref<InputEventWithModifiers> p_event);
 	void _cursor_orbit(Ref<InputEventWithModifiers> p_event);
+	Point2 _get_warped_mouse_motion(const Ref<InputEventMouseMotion> &p_event, Rect2 p_border) const;
 	Transform3D _get_cursor_transform();
 	void _reset_camera_3d();
 #endif


Test patch: