When I was implementing BASeTris, my Tetris Clone, I thought it would be nifty to have Controller support, so I could use my XBox One Controller that I have attached to my PC. My last adventure with Game Controllers ended poorly- BASeBlock has incredibly poor support for them, overall – In revisiting it with the consideration towards XInput rather than DirectInput this time, however, I eventually found XInput.Wrapper, which is a rather simple, single-class approach to handling XInput Keys.
The way that BASeTris handles input is my attempt at separating different Input methods from the start. The Game State interface has a single HandleGameKey routine which effectively handles a single press. That itself get’s called by the actual Input routines, which also include some additional management for features like DAS repeat for certain game keys. The XInput Wrapper, of course, was not like this. It is not particularly event driven and works differently.
I did mess about with it’s “Polling” feature for some time before eventually creating my own implementation of the same. The biggest thing I needed was a “translation” where I could see when keys were pressed and released and therefore track that information and translate it to appropriate GameKey presses. This was the rather small class that I settled on for this purpose and currently have implemented in BASeTris:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using XInput.Wrapper; namespace BASeTris { public class ControllerInputState { //tracks button state. Basically can be used to detect rising and falling edges of button presses. public EventHandler<ControllerButtonEventArgs> ButtonPressed; public EventHandler<ControllerButtonEventArgs> ButtonReleased; public X.Gamepad Pad { get; set; } = null; private HashSet<X.Gamepad.GamepadButtons> PressedButtons = new HashSet<X.Gamepad.GamepadButtons>(); //checks button states and raises events if necessary. public ControllerInputState(X.Gamepad pPad) { Pad = pPad; } /// <summary> /// returns the set of currently pressed buttons as a HashSet. /// </summary> /// <returns></returns> public ISet<X.Gamepad.GamepadButtons> GetPressedButtons() { return new HashSet<X.Gamepad.GamepadButtons>(PressedButtons); } public void CheckState() { if (Pad.Update()) { foreach (X.Gamepad.GamepadButtons iterate in Enum.GetValues(typeof(X.Gamepad.GamepadButtons))) { bool wasPressed = PressedButtons.Contains(iterate); bool Statepressed = GetButtonState(Pad, iterate); if (!wasPressed && Statepressed) { PressedButtons.Add(iterate); ButtonPressed?.Invoke(this, new ControllerButtonEventArgs(iterate)); } else if (wasPressed && !Statepressed) { PressedButtons.Remove(iterate); ButtonReleased?.Invoke(this, new ControllerButtonEventArgs(iterate)); } } } } private bool GetButtonState(X.Gamepad pad, X.Gamepad.GamepadButtons button) { return (((short) pad.Buttons & (short) button) == (short) button); } public class ControllerButtonEventArgs : EventArgs { public X.Gamepad.GamepadButtons button; public ControllerButtonEventArgs(X.Gamepad.GamepadButtons pButton) { button = pButton; } } } } |
It is a bit strange that I needed to create a wrapper for what is itself a wrapper, but it wasn’t like I was going to find a ready-made solution that integrated into how I had designed Input in BASeTris anyway- some massaging was therefore quite expected to be necessary.
Have something to say about this post? Comment!