From c98d7739aa3d79e283dccfea52437a6fef86e30c Mon Sep 17 00:00:00 2001 From: Nick Guy Date: Sun, 2 Jun 2024 17:10:15 +0100 Subject: [PATCH] Add custom input mapping on top of Godot's InputMap --- scenes/sandbox/player/SandboxPlayer.cs | 58 ++++++++---- scenes/sandbox/player/player_scene.tscn | 18 ++-- scenes/sandbox/sandbox_scene.tscn | 8 +- scripts/libs/input/IInputProvider.cs | 48 ++++++++++ scripts/libs/input/InputAction.cs | 112 ++++++++++++++++++++++++ scripts/libs/input/InputContext.cs | 65 ++++++++++++++ 6 files changed, 281 insertions(+), 28 deletions(-) create mode 100644 scripts/libs/input/IInputProvider.cs create mode 100644 scripts/libs/input/InputAction.cs create mode 100644 scripts/libs/input/InputContext.cs diff --git a/scenes/sandbox/player/SandboxPlayer.cs b/scenes/sandbox/player/SandboxPlayer.cs index 49a89c4..06a52e4 100644 --- a/scenes/sandbox/player/SandboxPlayer.cs +++ b/scenes/sandbox/player/SandboxPlayer.cs @@ -1,7 +1,10 @@ +using System.Collections.Generic; +using Carotrauma.scripts.libs.input; using Godot; -using System; -public partial class SandboxPlayer : CharacterBody3D +namespace Carotrauma.scenes.sandbox.player; + +public partial class SandboxPlayer : CharacterBody3D, IInputProvider { [Export] @@ -9,36 +12,52 @@ public partial class SandboxPlayer : CharacterBody3D [Export] public float JumpVelocity = 4.5f; + [Export] public float MouseSensitivity = 0.05f; + // Get the gravity from the project settings to be synced with RigidBody nodes. public float gravity = ProjectSettings.GetSetting("physics/3d/default_gravity").AsSingle(); private float camera_anglev = 0; + private Node3D pivot; private Camera3D cam; - public override void _Ready() { - cam = GetChild(3, true); + private KeyInputAction GrabCursor; + private KeyInputAction Jump; + private VectorInputAction Movement; + + public void CreateInputs() { + GrabCursor = this.KeyInput(Key.Escape); + Jump = this.KeyInput(Key.Space); + Movement = this.VectorKeyInput(Key.D, Key.A, Key.S, Key.W); } + public override void _EnterTree() { + CreateInputs(); + } - public static float ToRadians (float degrees) { - // Angle in 10th of a degree - return (float)((degrees * Math.PI)/180.0f); + public override void _Ready() { + pivot = GetChild(2, true); + cam = pivot.GetChild(0, true); } public override void _Input(InputEvent @event) { - if (@event is InputEventMouseMotion) { + if (@event is InputEventMouseMotion && Input.MouseMode == Input.MouseModeEnum.Captured) { var M = (InputEventMouseMotion)@event; - cam.RotateY(ToRadians(-M.Relative.X * 0.3f)); - var changeV = -M.Relative.Y * 0.3f; - if (camera_anglev + changeV > -50 && camera_anglev + changeV < 50) { - camera_anglev += changeV; - cam.RotateX(ToRadians(changeV)); - } - GetViewport().SetInputAsHandled(); + + pivot.RotateX(Mathf.DegToRad(-M.Relative.Y * MouseSensitivity)); + RotateY(Mathf.DegToRad(-M.Relative.X * MouseSensitivity)); + + Vector3 cameraRot = pivot.RotationDegrees; + cameraRot.X = Mathf.Clamp(cameraRot.X, -70, 70); + pivot.RotationDegrees = cameraRot; } } public override void _PhysicsProcess(double delta) { + + if (GrabCursor.IsJustPressed()) + Input.MouseMode = Input.MouseMode == Input.MouseModeEnum.Visible ? Input.MouseModeEnum.Captured : Input.MouseModeEnum.Visible; + Vector3 velocity = Velocity; // Add the gravity. @@ -46,12 +65,12 @@ public partial class SandboxPlayer : CharacterBody3D velocity.Y -= gravity * (float)delta; // Handle Jump. - if (Input.IsActionJustPressed("jump") && IsOnFloor()) + if (Jump.IsJustPressed() && IsOnFloor()) velocity.Y = JumpVelocity; // Get the input direction and handle the movement/deceleration. // As good practice, you should replace UI actions with custom gameplay actions. - Vector2 inputDir = Input.GetVector("move_left", "move_right", "move_forward", "move_back");; + Vector2 inputDir = Movement.GetStrength(); Vector3 direction = (Transform.Basis * new Vector3(inputDir.X, 0, inputDir.Y)).Normalized(); if (direction != Vector3.Zero) { @@ -67,4 +86,7 @@ public partial class SandboxPlayer : CharacterBody3D Velocity = velocity; MoveAndSlide(); } -} + + public List Actions => _actions; + private List _actions = new(); +} \ No newline at end of file diff --git a/scenes/sandbox/player/player_scene.tscn b/scenes/sandbox/player/player_scene.tscn index 6674ae8..e4c4191 100644 --- a/scenes/sandbox/player/player_scene.tscn +++ b/scenes/sandbox/player/player_scene.tscn @@ -1,13 +1,10 @@ -[gd_scene load_steps=4 format=3 uid="uid://v5iatrrwgror"] - -[ext_resource type="Script" path="res://scenes/sandbox/player/SandboxPlayer.cs" id="1_auraj"] +[gd_scene load_steps=3 format=3 uid="uid://v5iatrrwgror"] [sub_resource type="CapsuleMesh" id="CapsuleMesh_orax3"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_4fhb2"] [node name="Player" type="CharacterBody3D"] -script = ExtResource("1_auraj") [node name="CSGMesh3D" type="CSGMesh3D" parent="."] mesh = SubResource("CapsuleMesh_orax3") @@ -15,9 +12,12 @@ mesh = SubResource("CapsuleMesh_orax3") [node name="CollisionShape3D" type="CollisionShape3D" parent="."] shape = SubResource("CapsuleShape3D_4fhb2") -[node name="CSGBox3D" type="CSGBox3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.436351, 0.458557, 0) -size = Vector3(0.5, 0.25, 0.25) +[node name="Pivot" type="Node3D" parent="."] -[node name="Camera" type="Camera3D" parent="."] -transform = Transform3D(1, 0, 0, 0, 0.944686, 0.327976, 0, -0.327976, 0.944686, 0, 1.61751, 2.35265) +[node name="Camera" type="Camera3D" parent="Pivot"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0) + +[node name="CSGBox3D" type="CSGBox3D" parent="Pivot"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, -0.5) +visibility_range_begin = 0.5 +size = Vector3(0.25, 0.25, 0.5) diff --git a/scenes/sandbox/sandbox_scene.tscn b/scenes/sandbox/sandbox_scene.tscn index 844475a..851811e 100644 --- a/scenes/sandbox/sandbox_scene.tscn +++ b/scenes/sandbox/sandbox_scene.tscn @@ -1,6 +1,8 @@ -[gd_scene load_steps=6 format=3 uid="uid://c3hv3mdy52mar"] +[gd_scene load_steps=8 format=3 uid="uid://c3hv3mdy52mar"] [ext_resource type="PackedScene" uid="uid://v5iatrrwgror" path="res://scenes/sandbox/player/player_scene.tscn" id="1_6lr48"] +[ext_resource type="Script" path="res://scenes/sandbox/player/SandboxPlayer.cs" id="2_3mcgf"] +[ext_resource type="Script" path="res://scripts/libs/input/InputContext.cs" id="2_mp2vp"] [sub_resource type="BoxShape3D" id="BoxShape3D_tcgka"] size = Vector3(60, 2, 60) @@ -38,3 +40,7 @@ mesh = SubResource("SphereMesh_gsyqq") [node name="Player" parent="." instance=ExtResource("1_6lr48")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.04543, 0) +script = ExtResource("2_3mcgf") + +[node name="Node" type="Node" parent="Player"] +script = ExtResource("2_mp2vp") diff --git a/scripts/libs/input/IInputProvider.cs b/scripts/libs/input/IInputProvider.cs new file mode 100644 index 0000000..b48c226 --- /dev/null +++ b/scripts/libs/input/IInputProvider.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using Godot; + +namespace Carotrauma.scripts.libs.input; + +public interface IInputProvider { + + void CreateInputs(); + + List Actions { get; } + +} + +public static class IInputProviderExtensions { + public static KeyInputAction KeyInput(this IInputProvider self, Key key) { + var A = new KeyInputAction + { + Key = key + }; + self.Actions.Add(A); + return A; + } + + public static MouseButtonInputAction MouseButtonInput(this IInputProvider self, MouseButton button) { + var A = new MouseButtonInputAction + { + Button = button + }; + self.Actions.Add(A); + return A; + } + + public static AxisInputAction AxisKeyInput(this IInputProvider self, Key axisPos, + Key axisNeg) { + + var pos = self.KeyInput(axisPos); + var neg = self.KeyInput(axisNeg); + + return new AxisInputAction(pos.Name, neg.Name); + } + + public static VectorInputAction VectorKeyInput(this IInputProvider self, Key xPos, + Key xNeg, Key yPos, Key yNeg) { + + return new VectorInputAction(self.KeyInput(xPos), self.KeyInput(xNeg), self.KeyInput(yPos), self.KeyInput(yNeg)); + } +} \ No newline at end of file diff --git a/scripts/libs/input/InputAction.cs b/scripts/libs/input/InputAction.cs new file mode 100644 index 0000000..0b168a1 --- /dev/null +++ b/scripts/libs/input/InputAction.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using Godot; + +namespace Carotrauma.scripts.libs.input; + +public abstract class InputAction { + public string Name = Guid.NewGuid().ToString(); + + public InputEvent Event => getEvent(); + + protected abstract InputEvent getEvent(); + + public float GetStrength() { + return Input.GetActionStrength(Name); + } + + public static implicit operator String(InputAction a) { + return a.Name; + } + + public bool IsPressed() { + return Input.IsActionPressed(Name); + } + + public bool IsJustPressed() { + return Input.IsActionJustPressed(Name); + } + + public bool IsJustReleased() { + return Input.IsActionJustReleased(Name); + } +} + +public class InputAction : InputAction where T : new() { + + protected override InputEvent getEvent() { + var evt = new T(); + populateEvent(ref evt); + return evt as InputEvent; + } + + protected virtual void populateEvent(ref T evt) {} +} + +public class KeyInputAction : InputAction { + public Key Key; + + protected override void populateEvent(ref InputEventKey evt) { + evt.Keycode = Key; + } + // + // public bool IsPressed() { + // LastPolledPressed = Input.IsKeyPressed(Key); + // return LastPolledPressed; + // } + // + // public bool IsJustPressed() { + // bool Pressed = Input.IsActionPressed(Key); + // bool JustPressed = Pressed && !LastPolledPressed; + // LastPolledPressed = Pressed; + // return JustPressed; + // } + // + // public bool IsJustReleased() { + // bool Pressed = Input.IsKeyPressed(Key); + // bool JustReleased = !Pressed && LastPolledPressed; + // LastPolledPressed = Pressed; + // return JustReleased; + // } +} + +public class MouseButtonInputAction : InputAction { + public MouseButton Button; + + protected override void populateEvent(ref InputEventMouseButton evt) { + evt.ButtonIndex = Button; + } +} + +public class AxisInputAction { + + public AxisInputAction(string positiveActionName, string negativeActionName) { + PositiveActionName = positiveActionName; + NegativeActionName = negativeActionName; + } + + public string PositiveActionName; + public string NegativeActionName; + + public float GetStrength() { + return Input.GetAxis(NegativeActionName, PositiveActionName); + } +} + +public class VectorInputAction { + private readonly string _positiveXName; + private readonly string _negativeXName; + private readonly string _positiveYName; + private readonly string _negativeYName; + + public VectorInputAction(string positiveXName, string negativeXName, string positiveYName, string negativeYName) { + _positiveXName = positiveXName; + _negativeXName = negativeXName; + _positiveYName = positiveYName; + _negativeYName = negativeYName; + } + + public Vector2 GetStrength() { + return Input.GetVector(_negativeXName, _positiveXName, _negativeYName, _positiveYName); + } +} \ No newline at end of file diff --git a/scripts/libs/input/InputContext.cs b/scripts/libs/input/InputContext.cs new file mode 100644 index 0000000..a5a110b --- /dev/null +++ b/scripts/libs/input/InputContext.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Godot; + +namespace Carotrauma.scripts.libs.input; + +public partial class InputContext : Node { + + public override void _Ready() { + LoadContext(); + } + + public override void _ExitTree() { + UnloadContext(); + base._ExitTree(); + } + + private List _actions; + + private double cooldown = 1; + public override void _Process(double delta) { + cooldown -= delta; + if (cooldown > 0) return; + + cooldown = 5; + GD.Print(ToString()); + } + + public void LoadContext() { + var provider = GetParentOrNull(); + if (provider is not IInputProvider inputProvider) return; + _actions = inputProvider.Actions; + _actions.ForEach(LoadAction); + } + + public void UnloadContext() { + _actions?.ForEach(UnloadAction); + } + + private void LoadAction(InputAction action) { + GD.Print("Add action: " + action.Name); + InputMap.AddAction(action.Name); + GD.Print("Add event to action: " + action.Name); + InputMap.ActionAddEvent(action.Name, action.Event); + } + + private void UnloadAction(InputAction action) { + InputMap.ActionEraseEvent(action.Name, action.Event); + InputMap.EraseAction(action.Name); + } + + public override string ToString() { + if (_actions == null) return "No Actions"; + StringBuilder sb = new(); + sb.AppendLine("Input Context:"); + foreach (InputAction action in _actions) { + sb.Append(" - ").AppendLine(action.Name); + foreach (var inputEvent in InputMap.ActionGetEvents(action.Name)) + sb.Append(" - ").AppendLine(inputEvent.AsText()); + } + return sb.ToString(); + } + +}