-feat: input menu with saveable input settings

This commit is contained in:
rsxri 2025-04-22 23:51:12 +01:00
parent 30f9bfa5ff
commit 1003acd32d
7 changed files with 345 additions and 55 deletions

View file

@ -18,13 +18,14 @@ script = ExtResource("1_kyds1")
Faction = 2
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
scale = Vector2(1.3, 1.3)
shape = SubResource("CircleShape2D_6vq6f")
[node name="ShipSprite" type="Sprite2D" parent="."]
texture = ExtResource("1_4l75b")
[node name="LaserSpawn" type="Node2D" parent="."]
position = Vector2(0, -58)
position = Vector2(0, -75)
[node name="EffectSpawn" type="Node2D" parent="."]

View file

@ -1,93 +1,174 @@
[gd_scene format=3 uid="uid://01tjlnox4crb"]
[gd_scene load_steps=2 format=3 uid="uid://01tjlnox4crb"]
[node name="InputMenu" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 13.0
offset_top = 13.0
offset_right = 7.0
offset_bottom = 3.0
[ext_resource type="Script" path="res://script/input_menu.cs" id="1_u13wa"]
[node name="InputMenu" type="CenterContainer"]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -200.0
offset_top = -250.0
offset_right = 200.0
offset_bottom = 250.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_u13wa")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
[node name="ColorRect" type="ColorRect" parent="."]
custom_minimum_size = Vector2(400, 500)
layout_mode = 2
offset_right = 351.0
offset_bottom = 566.0
color = Color(0.233203, 0.22148, 0.205431, 1)
[node name="Forward" type="HBoxContainer" parent="VBoxContainer"]
[node name="Layout" type="VBoxContainer" parent="."]
custom_minimum_size = Vector2(400, 500)
layout_mode = 2
alignment = 1
[node name="Label" type="Label" parent="Layout"]
z_index = 1
layout_mode = 2
text = "Inputs"
horizontal_alignment = 1
uppercase = true
[node name="VSeparator" type="VSeparator" parent="Layout"]
z_index = -1
custom_minimum_size = Vector2(400, 35)
layout_mode = 2
[node name="InpFwdLabel" type="Label" parent="VBoxContainer/Forward"]
[node name="Forward" type="HBoxContainer" parent="Layout"]
layout_mode = 2
alignment = 1
[node name="InpFwdLabel" type="Label" parent="Layout/Forward"]
layout_mode = 2
text = "Forward"
[node name="InpFwd" type="Button" parent="VBoxContainer/Forward"]
[node name="InpFwd" type="Button" parent="Layout/Forward"]
layout_mode = 2
text = "W"
[node name="Backward" type="HBoxContainer" parent="VBoxContainer"]
[node name="Backward" type="HBoxContainer" parent="Layout"]
layout_mode = 2
alignment = 1
[node name="InpBckLabel" type="Label" parent="VBoxContainer/Backward"]
[node name="InpBckLabel" type="Label" parent="Layout/Backward"]
layout_mode = 2
text = "Backward"
[node name="InpBck" type="Button" parent="VBoxContainer/Backward"]
[node name="InpBack" type="Button" parent="Layout/Backward"]
layout_mode = 2
text = "S"
[node name="Left" type="HBoxContainer" parent="VBoxContainer"]
[node name="Left" type="HBoxContainer" parent="Layout"]
layout_mode = 2
alignment = 1
[node name="InpLftLabel" type="Label" parent="VBoxContainer/Left"]
[node name="InpLftLabel" type="Label" parent="Layout/Left"]
layout_mode = 2
text = "Left"
text = "Rotate Left"
[node name="InpLft" type="Button" parent="VBoxContainer/Left"]
[node name="InpLeft" type="Button" parent="Layout/Left"]
layout_mode = 2
text = "A"
[node name="Right" type="HBoxContainer" parent="VBoxContainer"]
[node name="Right" type="HBoxContainer" parent="Layout"]
layout_mode = 2
alignment = 1
[node name="InpRhtLabel" type="Label" parent="VBoxContainer/Right"]
[node name="InpRhtLabel" type="Label" parent="Layout/Right"]
layout_mode = 2
text = "Right"
text = "Rotate Right"
[node name="InpRht" type="Button" parent="VBoxContainer/Right"]
[node name="InpRight" type="Button" parent="Layout/Right"]
layout_mode = 2
text = "D"
[node name="Shoot" type="HBoxContainer" parent="VBoxContainer"]
[node name="StrafeL" type="HBoxContainer" parent="Layout"]
layout_mode = 2
alignment = 1
[node name="InpShootLabel" type="Label" parent="VBoxContainer/Shoot"]
[node name="InpSLLabel" type="Label" parent="Layout/StrafeL"]
layout_mode = 2
text = "Strafe Left
"
[node name="InpSL" type="Button" parent="Layout/StrafeL"]
layout_mode = 2
text = "Left"
[node name="StrafeR" type="HBoxContainer" parent="Layout"]
layout_mode = 2
alignment = 1
[node name="InpSRLabel" type="Label" parent="Layout/StrafeR"]
layout_mode = 2
text = "Strafe Right"
[node name="InpSR" type="Button" parent="Layout/StrafeR"]
layout_mode = 2
text = "Right"
[node name="Shoot" type="HBoxContainer" parent="Layout"]
layout_mode = 2
alignment = 1
[node name="InpShootLabel" type="Label" parent="Layout/Shoot"]
layout_mode = 2
text = "Shoot"
[node name="InpShoot" type="Button" parent="VBoxContainer/Shoot"]
[node name="InpShoot" type="Button" parent="Layout/Shoot"]
layout_mode = 2
text = "SPACE"
text = "Space"
[node name="Flight Assist" type="HBoxContainer" parent="VBoxContainer"]
[node name="Flight Assist" type="HBoxContainer" parent="Layout"]
layout_mode = 2
alignment = 1
[node name="InpFALabel" type="Label" parent="VBoxContainer/Flight Assist"]
[node name="InpFALabel" type="Label" parent="Layout/Flight Assist"]
layout_mode = 2
text = "Flight Assist"
[node name="InpFA" type="Button" parent="VBoxContainer/Flight Assist"]
[node name="InpFA" type="Button" parent="Layout/Flight Assist"]
layout_mode = 2
text = "F"
[node name="Back" type="Button" parent="VBoxContainer"]
[node name="Pause" type="HBoxContainer" parent="Layout"]
layout_mode = 2
alignment = 1
[node name="InpPauseLabel" type="Label" parent="Layout/Pause"]
layout_mode = 2
text = "Pause"
[node name="InpPause" type="Button" parent="Layout/Pause"]
layout_mode = 2
text = "P"
[node name="VSeparator3" type="VSeparator" parent="Layout"]
z_index = -1
custom_minimum_size = Vector2(400, 8)
layout_mode = 2
[node name="ListenLabel" type="Label" parent="Layout"]
z_index = 1
layout_mode = 2
text = "Listening..."
horizontal_alignment = 1
[node name="VSeparator2" type="VSeparator" parent="Layout"]
z_index = -1
custom_minimum_size = Vector2(400, 8)
layout_mode = 2
[node name="Back" type="Button" parent="Layout"]
layout_mode = 2
text = "Back
"
[node name="Save" type="Button" parent="VBoxContainer"]
[node name="Save" type="Button" parent="Layout"]
visible = false
layout_mode = 2
text = "Save"

View file

@ -1,6 +1,7 @@
[gd_scene load_steps=2 format=3 uid="uid://bh3heupvlc1ok"]
[gd_scene load_steps=3 format=3 uid="uid://bh3heupvlc1ok"]
[ext_resource type="Script" path="res://script/main_menu.cs" id="1_wxwq5"]
[ext_resource type="PackedScene" uid="uid://01tjlnox4crb" path="res://scenes/input_menu.tscn" id="2_2h615"]
[node name="MainMenu" type="Control"]
layout_mode = 3
@ -9,23 +10,37 @@ offset_right = 40.0
offset_bottom = 40.0
script = ExtResource("1_wxwq5")
[node name="StartButton" type="Button" parent="."]
[node name="Layout" type="VBoxContainer" parent="."]
layout_mode = 0
offset_right = 8.0
offset_bottom = 8.0
offset_right = 40.0
offset_bottom = 40.0
[node name="StartButton" type="Button" parent="Layout"]
layout_mode = 2
text = "Start Game
"
[node name="ExitButton" type="Button" parent="."]
layout_mode = 1
anchors_preset = -1
anchor_left = -1.52
anchor_top = 0.5
anchor_right = -1.52
anchor_bottom = 0.5
offset_left = 61.8
offset_top = 13.0
offset_right = 157.8
offset_bottom = 44.0
[node name="InputButton" type="Button" parent="Layout"]
layout_mode = 2
text = "Inputs
"
[node name="ExitButton" type="Button" parent="Layout"]
layout_mode = 2
text = "Exit Game
"
[node name="InputMenu" parent="." instance=ExtResource("2_2h615")]
visible = false
layout_mode = 0
anchors_preset = 0
anchor_left = 0.0
anchor_top = 0.0
anchor_right = 0.0
anchor_bottom = 0.0
offset_left = 770.0
offset_top = 270.0
offset_right = 1170.0
offset_bottom = 770.0
grow_horizontal = 1
grow_vertical = 1

View file

@ -16,6 +16,9 @@ public partial class ai_fighter : ship
public bool SignalsConnected { get; set; } = false;
private CollisionShape2D _hitbox;
public float HitboxModifier = 1f;
private Label _healthLabel;
private Label _hpLabel;
@ -29,6 +32,9 @@ public partial class ai_fighter : ship
public override void _Ready()
{
_hitbox = GetNode<CollisionShape2D>("CollisionShape2D");
_hitbox.Scale = new Vector2(HitboxModifier, HitboxModifier);
SetShipStats();
SetupVisual();
Sprite.Texture = GD.Load<Texture2D>(SpritePath);

175
MB_FYP/script/input_menu.cs Normal file
View file

@ -0,0 +1,175 @@
using Godot;
using System;
public partial class input_menu : CenterContainer
{
//public CenterContainer Menu;
private string awaitingAction;
private Label awaitingLabel;
private Button awaitingButton;
private Button _inpFwd;
private Button _inpBack;
private Button _inpLeft;
private Button _inpRight;
private Button _inpSR;
private Button _inpSL;
private Button _inpShoot;
private Button _inpFA;
private Button _inpPause;
public override void _Ready()
{
awaitingLabel = GetNode<Label>("Layout/ListenLabel");
awaitingLabel.Text = "";
GetNode<Button>("Layout/Back").Pressed += HideInputPanel;
GetNode<Button>("Layout/Save").Hide(); // hide save button - not needed really.
//input buttons
_inpFwd = GetNode<Button>("Layout/Forward/InpFwd");
_inpBack = GetNode<Button>("Layout/Backward/InpBack");
_inpLeft = GetNode<Button>("Layout/Left/InpLeft");
_inpRight = GetNode<Button>("Layout/Right/InpRight");
_inpSR = GetNode<Button>("Layout/StrafeR/InpSR");
_inpSL = GetNode<Button>("Layout/StrafeL/InpSL");
_inpShoot = GetNode<Button>("Layout/Shoot/InpShoot");
_inpFA = GetNode<Button>("Layout/Flight Assist/InpFA");
_inpPause = GetNode<Button>("Layout/Pause/InpPause");
_inpFwd.Pressed += () => StartRebind("up", _inpFwd);
_inpBack.Pressed += () => StartRebind("down", _inpBack);
_inpLeft.Pressed += () => StartRebind("left", _inpLeft);
_inpRight.Pressed += () => StartRebind("right", _inpRight);
_inpSR.Pressed += () => StartRebind("strafe_right", _inpSR);
_inpSL.Pressed += () => StartRebind("strafe_left", _inpSL);
_inpShoot.Pressed += () => StartRebind("shoot", _inpShoot);
_inpFA.Pressed += () => StartRebind("toggle_fa", _inpFA);
_inpPause.Pressed += () => StartRebind("pause", _inpPause);
LoadInputConfig();
UpdateButtonText();
}
private void UpdateButtonText()
{
_inpFwd.Text = GetKey("up");
_inpBack.Text = GetKey("down");
_inpLeft.Text = GetKey("left");
_inpRight.Text = GetKey("right");
_inpSR.Text = GetKey("strafe_right");
_inpSL.Text = GetKey("strafe_left");
_inpShoot.Text = GetKey("shoot");
_inpFA.Text = GetKey("toggle_fa");
_inpPause.Text = GetKey("pause");
}
private static string GetKey(string action)
{
var config = new ConfigFile();
var err = config.Load("user://config/input.cfg");
if (err != Error.Ok)
return "-";
int keycode = (int)config.GetValue("Input", action, 0);
if (keycode == 0)
return "-";
return OS.GetKeycodeString((Key)keycode);
}
private void SaveInputConfig(string path = "user://config/input.cfg")
{
var config = new ConfigFile();
config.SetValue("Input", "up", GetActionKeycode("up"));
config.SetValue("Input", "down", GetActionKeycode("down"));
config.SetValue("Input", "left", GetActionKeycode("left"));
config.SetValue("Input", "right", GetActionKeycode("right"));
config.SetValue("Input", "strafe_right", GetActionKeycode("strafe_right"));
config.SetValue("Input", "strafe_left", GetActionKeycode("strafe_left"));
config.SetValue("Input", "shoot", GetActionKeycode("shoot"));
config.SetValue("Input", "toggle_fa", GetActionKeycode("toggle_fa"));
config.SetValue("Input", "pause", GetActionKeycode("pause"));
config.Save(path);
}
private static void LoadInputConfig(string path = "user://config/input.cfg")
{
var config = new ConfigFile();
var err = config.Load(path);
if (err != Error.Ok)
{
//GD.Print("Failed to load input config");
return;
}
string[] actions = {"up", "down", "left", "right", "strafe_left", "strafe_right", "shoot", "toggle_fa", "pause"};
foreach (var action in actions)
{
InputMap.ActionEraseEvents(action);
int keycode = (int)config.GetValue("Input", action, 0);
//GD.Print(keycode);
if (keycode != 0)
{
var ev = new InputEventKey();
ev.Keycode = (Key)keycode;
ev.PhysicalKeycode = (Key)keycode;
InputMap.ActionAddEvent(action, ev);
//UpdateButtonText();
}
}
}
private static int GetActionKeycode(string action)
{
foreach (var e in InputMap.ActionGetEvents(action))
{
if (e is InputEventKey key) return (int)key.Keycode;
}
return 0;
}
public void ShowInputPanel()
{
this.Visible = true;
}
public void HideInputPanel()
{
this.Visible = false;
}
private void StartRebind(string action, Button button)
{
awaitingLabel.Text = "Listening...";
awaitingAction = action;
awaitingButton = button;
}
public override void _Input(InputEvent @event)
{
if (awaitingAction == null) return;
if (@event is not InputEventKey { Pressed: true, Echo: false } eventKey) return;
if (eventKey.Keycode == Key.Escape)
{
awaitingAction = null;
awaitingLabel.Text = "";
awaitingButton = null;
return;
}
InputMap.ActionEraseEvents(awaitingAction);
InputMap.ActionAddEvent(awaitingAction, eventKey);
awaitingButton.Text = eventKey.Keycode.ToString();
awaitingLabel.Text = "";
awaitingAction = null;
awaitingButton = null;
SaveInputConfig();
}
}

View file

@ -4,11 +4,16 @@ using System;
public partial class main_menu : Control
{
// Called when the node enters the scene tree for the first time.
private input_menu _inputMenu;
public override void _Ready()
{
_inputMenu = GetNode<input_menu>("InputMenu");
//Connecting Buttons
GetNode<Button>("StartButton").Pressed += OnStartButtonPressed;
GetNode<Button>("ExitButton").Pressed += OnExitButtonPressed;
GetNode<Button>("Layout/StartButton").Pressed += OnStartButtonPressed;
GetNode<Button>("Layout/ExitButton").Pressed += OnExitButtonPressed;
GetNode<Button>("Layout/InputButton").Pressed += OnInputButtonPressed;
}
private void OnStartButtonPressed()
@ -21,6 +26,11 @@ public partial class main_menu : Control
GetTree().Quit();
}
private void OnInputButtonPressed()
{
_inputMenu.Show();
}
// Called every frame. 'delta' has been the elapsed time since the previous frame.
public override void _Process(double delta)
{

View file

@ -47,11 +47,13 @@ public partial class wavecontroller : Node2D
enemy.GlobalPosition = spawn.GlobalPosition;
enemy.StatModifier = _aiStatModifier;
enemy.Scale = new Vector2(0.6f, 0.6f);
enemy.HitboxModifier = 1.6f; // make enemy hitbox a bit bigger (balance)
_enemyNode.AddChild(enemy);
}
}
if ((CurrWave - 1) % 5 == 0)
// spawn friendlies after 5 waves & if 1 or less remaining
if ((CurrWave - 1) % 5 == 0 && _friendlyNode.GetChildCount() <= 1)
{
GD.Print("attempting to spawn friendlies");
foreach (var spawn in _friendlySpawns)