diff --git a/Modules/Orama.Input/Bindings/GamepadButtonBinding.cs b/Modules/Orama.Input/Bindings/GamepadButtonBinding.cs new file mode 100644 index 0000000..d17fb3b --- /dev/null +++ b/Modules/Orama.Input/Bindings/GamepadButtonBinding.cs @@ -0,0 +1,27 @@ +// This file is part of the Orama Game Engine. +// Licensed under the MIT license. (https://github.com/Orama-Engine/Orama/blob/main/LICENSE) + +using Orama.Common; +using Orama.Input.Devices; + +namespace Orama.Input.Bindings; + +public class GamepadButtonBinding : IInputBinding +{ + Gamepad.Button Button { get; set; } + + public GamepadButtonBinding(Gamepad.Button button) + { + Button = button; + } + + public float Read() + { + var input = ModuleManager.GetModule(); + + if (input == null || input.PrimaryGamepad == null) + return 0f; + + return input.PrimaryGamepad.IsButtonPressed(Button) ? 1f : 0f; + } +} diff --git a/Modules/Orama.Input/Bindings/IInputBinding.cs b/Modules/Orama.Input/Bindings/IInputBinding.cs new file mode 100644 index 0000000..5a78447 --- /dev/null +++ b/Modules/Orama.Input/Bindings/IInputBinding.cs @@ -0,0 +1,13 @@ +// This file is part of the Orama Game Engine. +// Licensed under the MIT license. (https://github.com/Orama-Engine/Orama/blob/main/LICENSE) + +namespace Orama.Input.Bindings; + +/// +/// A single physical input source that can drive an . +/// +public interface IInputBinding +{ + /// Reads the current value of this binding. + public float Read(); +} diff --git a/Modules/Orama.Input/Bindings/KeyBinding.cs b/Modules/Orama.Input/Bindings/KeyBinding.cs new file mode 100644 index 0000000..da0860a --- /dev/null +++ b/Modules/Orama.Input/Bindings/KeyBinding.cs @@ -0,0 +1,27 @@ +// This file is part of the Orama Game Engine. +// Licensed under the MIT license. (https://github.com/Orama-Engine/Orama/blob/main/LICENSE) + +using Orama.Common; +using Orama.Input.Devices; + +namespace Orama.Input.Bindings; + +public class KeyBinding : IInputBinding +{ + Keyboard.Key Key { get; set; } + + public KeyBinding(Keyboard.Key key) + { + Key = key; + } + + public float Read() + { + var input = ModuleManager.GetModule(); + + if (input == null || input.PrimaryKeyboard == null) + return 0f; + + return input.PrimaryKeyboard.IsKeyDown(Key) ? 1f : 0f; + } +} diff --git a/Modules/Orama.Input/Bindings/MouseButtonBinding.cs b/Modules/Orama.Input/Bindings/MouseButtonBinding.cs new file mode 100644 index 0000000..096be18 --- /dev/null +++ b/Modules/Orama.Input/Bindings/MouseButtonBinding.cs @@ -0,0 +1,27 @@ +// This file is part of the Orama Game Engine. +// Licensed under the MIT license. (https://github.com/Orama-Engine/Orama/blob/main/LICENSE) + +using Orama.Common; +using Orama.Input.Devices; + +namespace Orama.Input.Bindings; + +public class MouseButtonBinding : IInputBinding +{ + Mouse.Button Button { get; set; } + + public MouseButtonBinding(Mouse.Button button) + { + Button = button; + } + + public float Read() + { + var input = ModuleManager.GetModule(); + + if (input == null || input.PrimaryMouse == null) + return 0f; + + return input.PrimaryMouse.IsButtonDown(Button) ? 1f : 0f; + } +} diff --git a/Modules/Orama.Input/InputAction.cs b/Modules/Orama.Input/InputAction.cs new file mode 100644 index 0000000..f6dd5ee --- /dev/null +++ b/Modules/Orama.Input/InputAction.cs @@ -0,0 +1,69 @@ +// This file is part of the Orama Game Engine. +// Licensed under the MIT license. (https://github.com/Orama-Engine/Orama/blob/main/LICENSE) + +using Orama.Input.Bindings; + +namespace Orama.Input; + +/// +/// A named input action resolved each frame from one or more s. +/// +public class InputAction +{ + /// The unique name used to look this action up via . + public string Name { get; } + + /// All bindings that can drive this action's value. + public List Bindings { get; } = new(); + + /// The resolved value for this frame. + public float Value { get; private set; } + + /// The resolved value from the previous frame. + public float PreviousValue { get; private set; } + + /// True while this action's value is nonzero. + public bool IsPressed => Value != 0f; + + /// Same as , evaluated against last frame's value. + public bool WasPressed => PreviousValue != 0f; + + /// True only on the frame this action goes from released to pressed. + public bool Triggered => IsPressed && !WasPressed; + + /// True only on the frame this action goes from pressed to released. + public bool Released => !IsPressed && WasPressed; + + public InputAction(string name) + { + Name = name; + } + + public InputAction(string name, params IInputBinding[] bindings) : this(name) + { + Bindings.AddRange(bindings); + } + + /// Adds a binding to this action. + public InputAction AddBinding(IInputBinding binding) + { + Bindings.Add(binding); + return this; + } + + /// Re-reads all bindings and resolves this action's value for the current frame. + internal void Update() + { + PreviousValue = Value; + + float value = 0f; + foreach (var binding in Bindings) + { + float bindingValue = binding.Read(); + if (bindingValue != 0f) + value = bindingValue; + } + + Value = value; + } +} diff --git a/Modules/Orama.Input/InputModule.cs b/Modules/Orama.Input/InputModule.cs index a254664..84e61bb 100644 --- a/Modules/Orama.Input/InputModule.cs +++ b/Modules/Orama.Input/InputModule.cs @@ -26,6 +26,7 @@ public class InputModule : BaseModule public Gamepad? PrimaryGamepad { get; private set; } private readonly List devices = new(); + private readonly Dictionary actions = new(); private IInputContext input = null!; @@ -52,6 +53,16 @@ public override void Initialize() PrimaryGamepad = devices.OfType().FirstOrDefault(); } + /// Registers an so it's updated each frame and can be looked up by name. + public InputAction RegisterAction(InputAction action) + { + actions[action.Name] = action; + return action; + } + + /// Gets a previously registered by name. + public InputAction GetAction(string name) => actions[name]; + /// public override void Dispose() { @@ -66,5 +77,8 @@ public void Update() { foreach (var device in devices) device.Update(); + + foreach (var action in actions.Values) + action.Update(); } } diff --git a/Players/Orama.Players.Desktop/Program.cs b/Players/Orama.Players.Desktop/Program.cs index 683aae3..4c2ca69 100644 --- a/Players/Orama.Players.Desktop/Program.cs +++ b/Players/Orama.Players.Desktop/Program.cs @@ -7,6 +7,7 @@ using Orama.Common.Utility; using Orama.GUI; using Orama.Input; +using Orama.Input.Bindings; using Orama.Math; using Orama.Physics; using Orama.Rendering; @@ -55,6 +56,10 @@ static void Main(string[] args) for (int i = 0; i < 1000; i++) OramaConsole.Log($"Logging {i} times."); + var testAction = new InputAction("TestAction"); + testAction.AddBinding(new KeyBinding(Input.Devices.Keyboard.Key.Space)); + testAction.AddBinding(new GamepadButtonBinding(Input.Devices.Gamepad.Button.ActionDown)); + ModuleManager.GetModule()?.RegisterAction(testAction); ModuleManager.GetModule()?.CurrentScene.StartAll(); }; @@ -66,6 +71,10 @@ static void Main(string[] args) Application.OnUpdate += () => { + var input = ModuleManager.GetModule(); + if (input!.GetAction("TestAction").Triggered) + OramaConsole.Log("Test action triggered"); + };