├── LICENSE ├── README.md └── src ├── Example ├── PlayerInput.cs └── PlayerInputFactory.cs ├── IInput.cs ├── IInputFactory.cs ├── InputEventInput.cs ├── InputFactory.cs ├── JoypadEventInput.cs └── KeyboardEventInput.cs /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Michael "Carter" Anderson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Input# # 2 | A C# abstraction on top of Godot's input events that makes life just a little bit easier (especially for local multiplayer games with multiple active controllers). This is still in its early stages. 3 | 4 | Disclaimers: 5 | * It's just a copy paste from my current project: it isn't a standalone library 6 | * It's relatively untested. Right now I can't / won't vouch for its ability to not destroy everything you love and care about 7 | * It currently uses IConvertible to let you use your own enums without using generics. I'm not sure I like this design but it works. -------------------------------------------------------------------------------- /src/Example/PlayerInput.cs: -------------------------------------------------------------------------------- 1 | // These are action names in my godot project's input map 2 | public enum PlayerInput 3 | { 4 | Fullscreen, 5 | Attack, 6 | Jump, 7 | Interact, 8 | StackDown, 9 | StackUp, 10 | Left, 11 | Right, 12 | Down, 13 | Up, 14 | AimLeft, 15 | AimRight, 16 | AimDown, 17 | AimUp, 18 | Quit, 19 | Shield, 20 | Misc, 21 | Drop, 22 | Horizontal, 23 | } -------------------------------------------------------------------------------- /src/Example/PlayerInputFactory.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | 3 | // This is an example of how I use it 4 | public class PlayerInputFactory : InputFactory 5 | { 6 | public override IInput ConvertEventToInput(InputEvent inputEvent) 7 | { 8 | var input = base.ConvertEventToInput(inputEvent); 9 | if (input is InputEventInput inputEventInput) 10 | { 11 | inputEventInput.RegisterMap(); 12 | inputEventInput.AddVirtualAxis(PlayerInput.Horizontal, PlayerInput.Left, PlayerInput.Right); 13 | } 14 | 15 | // you would probably do these things elsewhere, but here are some usage examples: 16 | 17 | // connect a signal 18 | input.Pressed += action => GD.Print($"{action} was just pressed"); 19 | 20 | // check the current state 21 | if (input.IsPressed(PlayerInput.Left)) 22 | { 23 | GD.Print($"{nameof(PlayerInput.Left)} is currently pressed") 24 | } 25 | 26 | return input; 27 | } 28 | } -------------------------------------------------------------------------------- /src/IInput.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using HighHat.Actors; 4 | 5 | public interface IInput 6 | { 7 | event Action Pressed; 8 | event Action Released; 9 | int DeviceID { get; } 10 | int InputID { get; } 11 | float GetAxis(IConvertible action); 12 | bool IsPressed(IConvertible action); 13 | } -------------------------------------------------------------------------------- /src/IInputFactory.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | 3 | public interface IInputFactory 4 | { 5 | IInput ConvertEventToInput(InputEvent inputEvent); 6 | int GetInputID(InputEvent inputEvent); 7 | } -------------------------------------------------------------------------------- /src/InputEventInput.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | public abstract class InputEventInput : Node, IInput 7 | { 8 | public int InputID { get; set; } 9 | public int DeviceID { get; set; } 10 | public Dictionary Actions = new Dictionary(); 11 | public Dictionary InputMap = new Dictionary(); 12 | public Dictionary> VirtualAxes = new Dictionary>(); 13 | private float _actionDeadZone = 0.8f; 14 | 15 | public event Action Pressed; 16 | public event Action Released; 17 | 18 | public InputEventInput(int inputID, int deviceID) 19 | { 20 | InputID = inputID; 21 | DeviceID = deviceID; 22 | } 23 | 24 | public void RegisterMap() where T : IConvertible 25 | { 26 | var enumType = typeof(T); 27 | foreach (var actionName in Enum.GetNames(enumType)) 28 | { 29 | foreach (var inputEvent in Godot.InputMap.GetActionList(actionName).OfType()) 30 | { 31 | if (HandleEvent(inputEvent).HasValue) 32 | { 33 | var enumValue = (T)Enum.Parse(enumType, actionName); 34 | InputMap.Add(enumValue, inputEvent); 35 | Actions[enumValue] = 0.0f; 36 | } 37 | } 38 | } 39 | } 40 | 41 | public float? HandleEvent(InputEvent inputEvent) 42 | { 43 | if (inputEvent.Device == DeviceID) 44 | { 45 | var eventValue = HandleEventInternal(inputEvent); 46 | return eventValue; 47 | } 48 | 49 | return null; 50 | } 51 | 52 | public abstract float? HandleEventInternal(InputEvent @event); 53 | public override void _Input(InputEvent @event) 54 | { 55 | var eventValue = HandleEvent(@event); 56 | if (eventValue.HasValue == false) 57 | { 58 | return; 59 | } 60 | 61 | foreach (var mapping in InputMap) 62 | { 63 | if (mapping.Value.ActionMatch(@event)) 64 | { 65 | if (eventValue.HasValue) 66 | { 67 | var pressed = IsPressed(mapping.Key); 68 | var willBePressed = IsInputValuePressed(eventValue.Value); 69 | if (pressed && willBePressed == false) // just released 70 | { 71 | Released?.Invoke(mapping.Key); 72 | } 73 | else if (pressed == false && willBePressed) // just pressed 74 | { 75 | Pressed?.Invoke(mapping.Key); 76 | } 77 | 78 | Actions[mapping.Key] = eventValue.Value; 79 | } 80 | } 81 | } 82 | } 83 | 84 | public void AddVirtualAxis(IConvertible name, IConvertible negativeAction, IConvertible positiveAction) 85 | { 86 | VirtualAxes[name] = new Tuple(negativeAction, positiveAction); 87 | } 88 | 89 | public float GetAxis(IConvertible action) 90 | { 91 | if (Actions.TryGetValue(action, out float axisAction)) 92 | { 93 | return axisAction; 94 | } 95 | else if (VirtualAxes.TryGetValue(action, out Tuple virtualAxis)) 96 | { 97 | var leftAxis = GetAxis(virtualAxis.Item1); 98 | var rightAxis = GetAxis(virtualAxis.Item2); 99 | 100 | return Math.Abs(rightAxis) - Math.Abs(leftAxis); 101 | } 102 | 103 | throw new Exception($"Action \"{action}\" does not exist"); 104 | } 105 | 106 | public bool IsPressed(IConvertible action) 107 | { 108 | if (Actions.TryGetValue(action, out float pressedAction)) 109 | { 110 | return IsInputValuePressed(pressedAction); 111 | } 112 | 113 | throw new Exception($"Action \"{action}\" does not exist"); 114 | } 115 | 116 | private bool IsInputValuePressed(float inputValue) 117 | { 118 | return inputValue > _actionDeadZone; 119 | } 120 | } -------------------------------------------------------------------------------- /src/InputFactory.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | 3 | namespace HighHat.Input 4 | { 5 | public class InputFactory : IInputFactory 6 | { 7 | public const int KeyboardEventIDOffset = 100; 8 | public const int JoypadEventIDOffset = 200; 9 | public virtual IInput ConvertEventToInput(InputEvent inputEvent) 10 | { 11 | var inputID = GetInputID(inputEvent); 12 | switch (inputEvent) 13 | { 14 | case InputEventKey inputEventKey: 15 | return new KeyboardEventInput(inputID, inputEvent.Device); 16 | case InputEventJoypadButton inputEventJoy: 17 | return new JoypadEventInput(inputID, inputEventJoy.Device); 18 | default: 19 | return null; 20 | } 21 | } 22 | 23 | public int GetInputID(InputEvent inputEvent) 24 | { 25 | switch (inputEvent) 26 | { 27 | case InputEventKey inputEventKey: 28 | return inputEvent.Device + KeyboardEventIDOffset; 29 | case InputEventJoypadButton inputEventJoy: 30 | return inputEvent.Device + JoypadEventIDOffset; 31 | default: 32 | return inputEvent.Device; 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/JoypadEventInput.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | 4 | namespace HighHat.Input 5 | { 6 | public class JoypadEventInput : InputEventInput 7 | { 8 | public JoypadEventInput(int inputID, int deviceID) : base(inputID, deviceID) 9 | { 10 | } 11 | 12 | public override float? HandleEventInternal(InputEvent @event) 13 | { 14 | switch (@event) 15 | { 16 | case InputEventJoypadButton buttonEvent: 17 | return buttonEvent.Pressed ? 1.0f : 0.0f; 18 | case InputEventJoypadMotion motionEvent: 19 | return Mathf.Abs(motionEvent.AxisValue); 20 | default: 21 | return null; 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/KeyboardEventInput.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | 4 | namespace HighHat.Input 5 | { 6 | public class KeyboardEventInput : InputEventInput 7 | { 8 | public KeyboardEventInput(int inputID, int deviceID) : base(inputID, deviceID) 9 | { 10 | } 11 | 12 | public override float? HandleEventInternal(InputEvent @event) 13 | { 14 | if (@event is InputEventKey keyEvent) 15 | { 16 | return keyEvent.Pressed ? 1.0f : 0.0f; 17 | } 18 | 19 | return null; 20 | } 21 | } 22 | } --------------------------------------------------------------------------------