From 38c86f1a67de38f2f35e69c557626fb054ad2107 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Thu, 19 Mar 2026 03:47:15 +0100 Subject: [PATCH] feat: gameplay settings screen (deadzone + sensitivity) --- objects/ui/gameplay_settings.tscn | 97 +++++++++++++++++ objects/ui/settings_menu.tscn | 1 - scenes/main_menu.tscn | 10 +- scripts/UI/GameplaySettings.cs | 101 ++++++++++++++++++ .../components/Movement/PlayerInputHandler.cs | 7 +- 5 files changed, 208 insertions(+), 8 deletions(-) create mode 100644 objects/ui/gameplay_settings.tscn create mode 100644 scripts/UI/GameplaySettings.cs diff --git a/objects/ui/gameplay_settings.tscn b/objects/ui/gameplay_settings.tscn new file mode 100644 index 0000000..c63f6fd --- /dev/null +++ b/objects/ui/gameplay_settings.tscn @@ -0,0 +1,97 @@ +[gd_scene load_steps=3 format=3 uid="uid://gameplay_settings_scene"] + +[ext_resource type="Script" uid="uid://gameplay_settings_script" path="res://scripts/UI/GameplaySettings.cs" id="1_gameplay"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_gameplay"] +bg_color = Color(0, 0, 0, 1) + +[node name="Gameplay Settings" type="Control" node_paths=PackedStringArray("DeadzoneSlider", "DeadzoneValueLabel", "SensitivitySlider", "SensitivityValueLabel", "GameplaySettingsControl")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 6 +size_flags_vertical = 6 +script = ExtResource("1_gameplay") +DeadzoneSlider = NodePath("PanelContainer/MarginContainer/VBoxContainer/Deadzone/HSlider") +DeadzoneValueLabel = NodePath("PanelContainer/MarginContainer/VBoxContainer/Deadzone/HBoxContainer/DeadzoneValueLabel") +SensitivitySlider = NodePath("PanelContainer/MarginContainer/VBoxContainer/Sensitivity/HSlider") +SensitivityValueLabel = NodePath("PanelContainer/MarginContainer/VBoxContainer/Sensitivity/HBoxContainer/SensitivityValueLabel") +GameplaySettingsControl = NodePath(".") + +[node name="PanelContainer" type="PanelContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_gameplay") + +[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 8 +theme_override_constants/margin_top = 8 +theme_override_constants/margin_right = 8 +theme_override_constants/margin_bottom = 8 + +[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 + +[node name="Gameplay" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "GAMEPLAY" +horizontal_alignment = 1 +vertical_alignment = 1 +uppercase = true + +[node name="Spacer" type="Control" parent="PanelContainer/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 +size_flags_vertical = 3 + +[node name="Deadzone" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 4 + +[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/Deadzone"] +layout_mode = 2 + +[node name="DeadzoneLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/Deadzone/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "GAMEPAD_DEADZONE" + +[node name="DeadzoneValueLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/Deadzone/HBoxContainer"] +layout_mode = 2 +text = "0.20" + +[node name="HSlider" type="HSlider" parent="PanelContainer/MarginContainer/VBoxContainer/Deadzone"] +custom_minimum_size = Vector2(64, 0) +layout_mode = 2 +value = 0.2 + +[node name="Sensitivity" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 4 + +[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/Sensitivity"] +layout_mode = 2 + +[node name="SensitivityLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/Sensitivity/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "GAMEPAD_SENSITIVITY" + +[node name="SensitivityValueLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/Sensitivity/HBoxContainer"] +layout_mode = 2 +text = "1.00" + +[node name="HSlider" type="HSlider" parent="PanelContainer/MarginContainer/VBoxContainer/Sensitivity"] +custom_minimum_size = Vector2(64, 0) +layout_mode = 2 +value = 1.0 diff --git a/objects/ui/settings_menu.tscn b/objects/ui/settings_menu.tscn index faa984a..f5a956a 100644 --- a/objects/ui/settings_menu.tscn +++ b/objects/ui/settings_menu.tscn @@ -68,7 +68,6 @@ flat = true [node name="Gameplay Settings Button" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"] layout_mode = 2 -disabled = true text = "GAMEPLAY_BUTTON" flat = true diff --git a/scenes/main_menu.tscn b/scenes/main_menu.tscn index af2e1ff..5c0d77a 100644 --- a/scenes/main_menu.tscn +++ b/scenes/main_menu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=7 format=3 uid="uid://cl00e2ocomk3m"] +[gd_scene load_steps=8 format=3 uid="uid://cl00e2ocomk3m"] [ext_resource type="PackedScene" uid="uid://8b6ol5sssbgo" path="res://objects/ui/main_menu.tscn" id="1_ekxnf"] [ext_resource type="PackedScene" uid="uid://y0ae6e7t70fj" path="res://objects/ui/settings_menu.tscn" id="2_bqqt6"] @@ -6,6 +6,7 @@ [ext_resource type="PackedScene" uid="uid://b5fx1vdfky307" path="res://objects/ui/audio_settings.tscn" id="4_8ln24"] [ext_resource type="PackedScene" uid="uid://cvfsbiy5ggrpg" path="res://objects/ui/input_settings.tscn" id="5_rtw2f"] [ext_resource type="PackedScene" uid="uid://display_settings_scene" path="res://objects/ui/display_settings.tscn" id="6_dispset"] +[ext_resource type="PackedScene" uid="uid://gameplay_settings_scene" path="res://objects/ui/gameplay_settings.tscn" id="7_gameplay"] [node name="Main menu" type="CanvasLayer"] @@ -29,11 +30,8 @@ visible = false [node name="Input Settings" parent="." instance=ExtResource("5_rtw2f")] visible = false -[node name="Gameplay Settings" type="Control" parent="."] -layout_mode = 3 -anchors_preset = 0 -offset_right = 40.0 -offset_bottom = 40.0 +[node name="Gameplay Settings" parent="." instance=ExtResource("7_gameplay")] +visible = false [node name="Display Settings" parent="." instance=ExtResource("6_dispset")] visible = false diff --git a/scripts/UI/GameplaySettings.cs b/scripts/UI/GameplaySettings.cs new file mode 100644 index 0000000..52905c8 --- /dev/null +++ b/scripts/UI/GameplaySettings.cs @@ -0,0 +1,101 @@ +using Godot; +using Mr.BrickAdventures.Autoloads; + +namespace Mr.BrickAdventures.scripts.UI; + +public partial class GameplaySettings : Control +{ + [Export] public HSlider DeadzoneSlider { get; set; } + [Export] public Label DeadzoneValueLabel { get; set; } + [Export] public HSlider SensitivitySlider { get; set; } + [Export] public Label SensitivityValueLabel { get; set; } + [Export] public Control GameplaySettingsControl { get; set; } + + private UIManager UIManager => UIManager.Instance; + + public override void _Ready() + { + DeadzoneSlider.FocusMode = Control.FocusModeEnum.All; + SensitivitySlider.FocusMode = Control.FocusModeEnum.All; + + DeadzoneSlider.MinValue = 0.05; + DeadzoneSlider.MaxValue = 0.5; + DeadzoneSlider.Step = 0.05; + DeadzoneSlider.Value = 0.2; + + SensitivitySlider.MinValue = 0.5; + SensitivitySlider.MaxValue = 2.0; + SensitivitySlider.Step = 0.1; + SensitivitySlider.Value = 1.0; + + LoadSettings(); + + DeadzoneSlider.ValueChanged += OnDeadzoneChanged; + SensitivitySlider.ValueChanged += OnSensitivityChanged; + + if (InputDeviceManager.Instance != null) + InputDeviceManager.Instance.DeviceChanged += OnDeviceChanged; + GameplaySettingsControl.VisibilityChanged += OnVisibilityChanged; + } + + public override void _ExitTree() + { + if (InputDeviceManager.Instance != null) + InputDeviceManager.Instance.DeviceChanged -= OnDeviceChanged; + GameplaySettingsControl.VisibilityChanged -= OnVisibilityChanged; + SaveSettings(); + } + + public override void _UnhandledInput(InputEvent @event) + { + if (!@event.IsActionReleased("ui_cancel")) return; + if (!UIManager.IsScreenOnTop(GameplaySettingsControl)) return; + + SaveSettings(); + UIManager.PopScreen(); + } + + private void OnVisibilityChanged() + { + if (GameplaySettingsControl.IsVisibleInTree() && InputDeviceManager.Instance?.IsPointerless == true) + GrabFirstFocus(); + } + + private void OnDeviceChanged(int device) + { + var d = (InputDeviceManager.InputDevice)device; + if (d != InputDeviceManager.InputDevice.Mouse && GameplaySettingsControl.IsVisibleInTree()) + GrabFirstFocus(); + } + + private void GrabFirstFocus() => DeadzoneSlider.GrabFocus(); + + private void OnDeadzoneChanged(double value) + { + SettingsManager.Instance.GamepadDeadzone = (float)value; + foreach (var action in new[] { "left", "right", "up", "down" }) + { + if (InputMap.HasAction(action)) + InputMap.ActionSetDeadzone(action, (float)value); + } + DeadzoneValueLabel.Text = $"{value:F2}"; + SaveSettings(); + } + + private void OnSensitivityChanged(double value) + { + SettingsManager.Instance.GamepadSensitivity = (float)value; + SensitivityValueLabel.Text = $"{value:F2}"; + SaveSettings(); + } + + private void LoadSettings() + { + DeadzoneSlider.Value = SettingsManager.Instance.GamepadDeadzone; + SensitivitySlider.Value = SettingsManager.Instance.GamepadSensitivity; + DeadzoneValueLabel.Text = $"{SettingsManager.Instance.GamepadDeadzone:F2}"; + SensitivityValueLabel.Text = $"{SettingsManager.Instance.GamepadSensitivity:F2}"; + } + + private void SaveSettings() => SettingsManager.Instance.SaveGameplaySettings(); +} diff --git a/scripts/components/Movement/PlayerInputHandler.cs b/scripts/components/Movement/PlayerInputHandler.cs index 56aa1df..2d1946c 100644 --- a/scripts/components/Movement/PlayerInputHandler.cs +++ b/scripts/components/Movement/PlayerInputHandler.cs @@ -1,4 +1,5 @@ using Godot; +using Mr.BrickAdventures.Autoloads; namespace Mr.BrickAdventures.scripts.components; @@ -15,7 +16,11 @@ public partial class PlayerInputHandler : Node public override void _PhysicsProcess(double delta) { - MoveDirection = Input.GetVector("left", "right", "up", "down"); + var rawInput = Input.GetVector("left", "right", "up", "down"); + var sensitivity = SettingsManager.Instance?.GamepadSensitivity ?? 1.0f; + MoveDirection = rawInput.Length() > 0 + ? rawInput.Normalized() * Mathf.Min(rawInput.Length() * sensitivity, 1.0f) + : Vector2.Zero; JumpPressed = Input.IsActionJustPressed("jump"); JumpReleased = Input.IsActionJustReleased("jump");