Skip to content

Instantly share code, notes, and snippets.

@hyblocker
Last active August 7, 2023 14:30
Show Gist options
  • Save hyblocker/327210a04efb5b3d357d33f24318b522 to your computer and use it in GitHub Desktop.
Save hyblocker/327210a04efb5b3d357d33f24318b522 to your computer and use it in GitHub Desktop.
An Input Manager that supports keyboard, mouse and controller bindings, and allows for rebinding of Keybinds
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using System.Collections.Generic;
using System;
namespace Experiments
{
public static class InputManager
{
#region Variables
private static KeyboardState CurrentKeys;
private static KeyboardState PreviousKeys;
private static MouseState CurrentMouse;
private static MouseState PreviousMouse;
private static GamePadState CurrentGamepad;
private static GamePadState PreviousGamepad;
#region Misc Mouse Properties
public static int MouseX
{
get
{
return CurrentMouse.X;
}
}
public static int MouseY
{
get
{
return CurrentMouse.Y;
}
}
public static int MouseDeltaX
{
get
{
return PreviousMouse.X - CurrentMouse.X;
}
}
public static int MouseDeltaY
{
get
{
return PreviousMouse.Y - CurrentMouse.Y;
}
}
public static int MouseScrollNotchesY
{
get
{
return CurrentMouse.ScrollWheelValue - PreviousMouse.ScrollWheelValue;
}
}
public static int MouseScrollNotchesX
{
get
{
return CurrentMouse.HorizontalScrollWheelValue - PreviousMouse.HorizontalScrollWheelValue;
}
}
public static int MouseScrollWheel
{
get
{
return CurrentMouse.ScrollWheelValue - PreviousMouse.ScrollWheelValue;
}
}
#endregion
/// <summary>
/// The Gamepad port to use
/// </summary>
public static PlayerIndex GamepadPort = PlayerIndex.One;
/// <summary>
/// Whether a gamepad is connected and recognised by the game
/// </summary>
public static bool IsGamepadConnected = false;
/// <summary>
/// Whether to use gamepad for input
/// </summary>
public static bool UseGamepad = false;
private static Dictionary<string, Keybind> keybinds = new Dictionary<string, Keybind>();
private static bool _centerMouse = false;
#endregion
#region Initialisation
/// <summary>
/// Registers keybinds
/// </summary>
public static void Initialise()
{
//Movement
BindKey("move.forward", new Keybind(Keys.W, Buttons.LeftThumbstickUp));
BindKey("move.back", new Keybind(Keys.S, Buttons.LeftThumbstickDown));
BindKey("move.left", new Keybind(Keys.A, Buttons.LeftThumbstickLeft));
BindKey("move.right", new Keybind(Keys.D, Buttons.LeftThumbstickRight));
//Controller look
BindKey("look.forward", new Keybind(Buttons.RightThumbstickUp));
BindKey("look.back", new Keybind(Buttons.RightThumbstickDown));
BindKey("look.left", new Keybind(Buttons.RightThumbstickLeft));
BindKey("look.right", new Keybind(Buttons.RightThumbstickRight));
//Block interaction
BindKey("item.break", new Keybind(MouseButton.Button0, Buttons.RightTrigger));
BindKey("item.use", new Keybind(MouseButton.Button1, Buttons.LeftTrigger));
//Movement modifiers
BindKey("move.jump", new Keybind(Keys.Space, Buttons.A));
BindKey("move.sneak", new Keybind(Keys.LeftShift, Buttons.RightStick));
BindKey("move.sprint", new Keybind(Keys.LeftControl, Buttons.LeftStick));
//Controller hotbar scrolling
BindKey("hotbar.left", new Keybind(Buttons.LeftShoulder));
BindKey("hotbar.right", new Keybind(Buttons.RightShoulder));
//Controller pausing
BindKey("misc.pause", new Keybind(Keys.Escape, Buttons.Start));
BindKey("misc.screenshot", new Keybind(Keys.F2, Buttons.Back));
}
private static void BindKey(string name, Keybind fallback)
{
if (!keybinds.ContainsKey(name.ToLower()))
{
//try to load the keybind from file
keybinds.Add(name.ToLower(), fallback);
}
else
{
Console.Error.WriteLine("An error occured when registering keybind \"" + name.ToLower() + "\"! (ERR_KEYBIND_CONFLICT)");
}
}
/// <summary>
/// Rebinds a key to the given Keybind
/// </summary>
/// <param name="name"></param>
/// <param name="newBinding"></param>
public static void RebindKey(string name, Keybind newBinding)
{
if (keybinds.ContainsKey(name.ToLower()))
{
Keybind toBind = keybinds[name.ToLower()];
toBind.keyboardBinding = newBinding.keyboardBinding;
toBind.gamepadBinding = newBinding.gamepadBinding;
toBind.mouseBinding = newBinding.mouseBinding;
keybinds[name.ToLower()] = toBind;
}
}
/// <summary>
/// Returns a keybind representing the currently pressed key
/// </summary>
/// <returns></returns>
public static Keybind CaptureBinding()
{
Keybind currentBinding = new Keybind();
//Get the first element in the current keyboard keys
currentBinding.keyboardBinding = CurrentKeys.GetPressedKeys()[0];
//Get the first element in the current gamepad state
currentBinding.gamepadBinding = GetGamepadButtons();
//Get the first element in the current mouse state
currentBinding.mouseBinding = GetMouseButtons();
return currentBinding;
}
#region Current getters
private static Buttons GetGamepadButtons()
{
Buttons buttons = new Buttons();
//DPad
if (CurrentGamepad.IsButtonDown(Buttons.DPadUp))
{
buttons = Buttons.DPadUp;
}
else if (CurrentGamepad.IsButtonDown(Buttons.DPadDown))
{
buttons = Buttons.DPadDown;
}
else if (CurrentGamepad.IsButtonDown(Buttons.DPadLeft))
{
buttons = Buttons.DPadLeft;
}
else if (CurrentGamepad.IsButtonDown(Buttons.DPadRight))
{
buttons = Buttons.DPadRight;
}
//Start and Back
else if (CurrentGamepad.IsButtonDown(Buttons.Start))
{
buttons = Buttons.Start;
}
else if (CurrentGamepad.IsButtonDown(Buttons.Back))
{
buttons = Buttons.Back;
}
//Shoulders and Triggers
else if (CurrentGamepad.IsButtonDown(Buttons.LeftShoulder))
{
buttons = Buttons.LeftShoulder;
}
else if (CurrentGamepad.IsButtonDown(Buttons.RightShoulder))
{
buttons = Buttons.RightShoulder;
}
else if (CurrentGamepad.IsButtonDown(Buttons.RightTrigger))
{
buttons = Buttons.RightTrigger;
}
else if (CurrentGamepad.IsButtonDown(Buttons.LeftTrigger))
{
buttons = Buttons.LeftTrigger;
}
//Big button
else if (CurrentGamepad.IsButtonDown(Buttons.BigButton))
{
buttons = Buttons.BigButton;
}
//Action buttons
else if (CurrentGamepad.IsButtonDown(Buttons.A))
{
buttons = Buttons.A;
}
else if (CurrentGamepad.IsButtonDown(Buttons.B))
{
buttons = Buttons.B;
}
else if (CurrentGamepad.IsButtonDown(Buttons.X))
{
buttons = Buttons.X;
}
else if (CurrentGamepad.IsButtonDown(Buttons.Y))
{
buttons = Buttons.Y;
}
//Stick Clicks
else if (CurrentGamepad.IsButtonDown(Buttons.LeftStick))
{
buttons = Buttons.LeftStick;
}
else if (CurrentGamepad.IsButtonDown(Buttons.RightStick))
{
buttons = Buttons.RightStick;
}
//Right thumbstick
else if (CurrentGamepad.IsButtonDown(Buttons.RightThumbstickUp))
{
buttons = Buttons.RightThumbstickUp;
}
else if (CurrentGamepad.IsButtonDown(Buttons.RightThumbstickDown))
{
buttons = Buttons.RightThumbstickDown;
}
else if (CurrentGamepad.IsButtonDown(Buttons.RightThumbstickRight))
{
buttons = Buttons.RightThumbstickRight;
}
else if (CurrentGamepad.IsButtonDown(Buttons.RightThumbstickLeft))
{
buttons = Buttons.RightThumbstickLeft;
}
//Left thumbstick
else if (CurrentGamepad.IsButtonDown(Buttons.LeftThumbstickUp))
{
buttons = Buttons.LeftThumbstickUp;
}
else if (CurrentGamepad.IsButtonDown(Buttons.LeftThumbstickDown))
{
buttons = Buttons.LeftThumbstickDown;
}
else if (CurrentGamepad.IsButtonDown(Buttons.LeftThumbstickRight))
{
buttons = Buttons.LeftThumbstickRight;
}
else if (CurrentGamepad.IsButtonDown(Buttons.LeftThumbstickLeft))
{
buttons = Buttons.LeftThumbstickLeft;
}
return buttons;
}
private static MouseButton GetMouseButtons()
{
MouseButton mouse = new MouseButton();
//Check the button
if (CurrentMouse.LeftButton == ButtonState.Pressed)
{
mouse = MouseButton.Button0;
}
else if (CurrentMouse.RightButton == ButtonState.Pressed)
{
mouse = MouseButton.Button1;
}
else if (CurrentMouse.MiddleButton == ButtonState.Pressed)
{
mouse = MouseButton.Button2;
}
else if (CurrentMouse.XButton1 == ButtonState.Pressed)
{
mouse = MouseButton.Button3;
}
else if (CurrentMouse.XButton2 == ButtonState.Pressed)
{
mouse = MouseButton.Button4;
}
return mouse;
}
#endregion
#endregion
#region Input Handling
/// <summary>
/// Centers the mouse
/// </summary>
public static void CenterMouse()
{
_centerMouse = true;
}
/// <summary>
/// Returns whether the keybind is currently pressed.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static bool IsPressed(string name)
{
if (keybinds.ContainsKey(name.ToLower()))
{
return IsKeybindPressedCurrent(keybinds[name.ToLower()]);
}
Console.Error.WriteLine("Keybind \"" + name.ToLower() + "\" doesn't exist! (ERR_KEYBIND_NULL)");
return false;
}
/// <summary>
/// Returns whether the keybind was released
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static bool Released(string name)
{
if (keybinds.ContainsKey(name.ToLower()))
{
return IsKeybindPressedCurrent(keybinds[name.ToLower()]) == false && IsKeybindPressedPrevious(keybinds[name.ToLower()]) == true;
}
Console.Error.WriteLine("Keybind \"" + name.ToLower() + "\" doesn't exist! (ERR_KEYBIND_NULL)");
return false;
}
/// <summary>
/// Returns whether the keybind started being pressed
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static bool PressedStart(string name)
{
if (keybinds.ContainsKey(name.ToLower()))
{
return IsKeybindPressedCurrent(keybinds[name.ToLower()]) == true && IsKeybindPressedPrevious(keybinds[name.ToLower()]) == false;
}
Console.Error.WriteLine("Keybind \"" + name.ToLower() + "\" doesn't exist! (ERR_KEYBIND_NULL)");
return false;
}
#endregion
#region Keybind Checks
private static bool IsKeybindPressedCurrent(Keybind keybind)
{
bool isPressed = false;
//if (IsGamepadConnected && UseGamepad)
if (UseGamepad)
{
isPressed = CurrentGamepad.IsButtonDown(keybind.gamepadBinding);
}
if (!keybind.GamepadOnly)
{
if (!keybind.PreferKeyboard)
{
//Mouse keybinds
isPressed = isPressed || keybind.mouseBinding == MouseButton.Button0 && CurrentMouse.LeftButton == ButtonState.Pressed;
isPressed = isPressed || keybind.mouseBinding == MouseButton.Button1 && CurrentMouse.RightButton == ButtonState.Pressed;
isPressed = isPressed || keybind.mouseBinding == MouseButton.Button2 && CurrentMouse.MiddleButton == ButtonState.Pressed;
isPressed = isPressed || keybind.mouseBinding == MouseButton.Button3 && CurrentMouse.XButton1 == ButtonState.Pressed;
isPressed = isPressed || keybind.mouseBinding == MouseButton.Button4 && CurrentMouse.XButton2 == ButtonState.Pressed;
}
//Keyboard binding
isPressed = isPressed || CurrentKeys.IsKeyDown(keybind.keyboardBinding);
}
return isPressed;
}
private static bool IsKeybindPressedPrevious(Keybind keybind)
{
bool isPressed = false;
//if (IsGamepadConnected && UseGamepad)
if (UseGamepad)
{
isPressed = PreviousGamepad.IsButtonDown(keybind.gamepadBinding);
}
if (!keybind.GamepadOnly)
{
if (!keybind.PreferKeyboard)
{
//Mouse keybinds
isPressed = isPressed || keybind.mouseBinding == MouseButton.Button0 && PreviousMouse.LeftButton == ButtonState.Pressed;
isPressed = isPressed || keybind.mouseBinding == MouseButton.Button1 && PreviousMouse.RightButton == ButtonState.Pressed;
isPressed = isPressed || keybind.mouseBinding == MouseButton.Button2 && PreviousMouse.MiddleButton == ButtonState.Pressed;
isPressed = isPressed || keybind.mouseBinding == MouseButton.Button3 && PreviousMouse.XButton1 == ButtonState.Pressed;
isPressed = isPressed || keybind.mouseBinding == MouseButton.Button4 && PreviousMouse.XButton2 == ButtonState.Pressed;
}
//Keyboard binding
isPressed = isPressed || PreviousKeys.IsKeyDown(keybind.keyboardBinding);
}
return isPressed;
}
#endregion
#region Update
public static void Update()
{
//Set the current state
CurrentKeys = Keyboard.GetState();
CurrentMouse = Mouse.GetState();
}
public static void FinalUpdate()
{
if (_centerMouse)
{
Mouse.SetPosition(VoxelClient.Graphics.GraphicsDevice.PresentationParameters.BackBufferWidth / 2, VoxelClient.Graphics.GraphicsDevice.PresentationParameters.BackBufferHeight / 2);
_centerMouse = false;
}
//Save the current state
PreviousKeys = CurrentKeys;
PreviousMouse = Mouse.GetState();
//Gamepad handling
for (PlayerIndex i = PlayerIndex.One; i <= PlayerIndex.Four; i++)
{
GamePadCapabilities state = GamePad.GetCapabilities(i);
if (state.IsConnected)
{
GamepadPort = i;
}
}
GamePadCapabilities capabilities = GamePad.GetCapabilities(GamepadPort);
IsGamepadConnected = capabilities.IsConnected;
//if (IsGamepadConnected)
{
PreviousGamepad = CurrentGamepad;
CurrentGamepad = GamePad.GetState(GamepadPort);
UseGamepad = UseGamepad || CurrentGamepad.IsButtonDown(
//If any button is down currently
Buttons.DPadUp | Buttons.DPadDown | Buttons.DPadLeft | Buttons.DPadRight |
Buttons.Start | Buttons.Back | Buttons.LeftStick | Buttons.RightStick |
Buttons.LeftShoulder | Buttons.RightShoulder | Buttons.BigButton |
Buttons.A | Buttons.B | Buttons.X | Buttons.Y |
Buttons.RightTrigger | Buttons.LeftTrigger |
Buttons.RightThumbstickUp | Buttons.RightThumbstickDown | Buttons.RightThumbstickRight | Buttons.RightThumbstickLeft |
Buttons.LeftThumbstickLeft | Buttons.LeftThumbstickUp | Buttons.LeftThumbstickDown | Buttons.LeftThumbstickRight);
}
}
#endregion
#region Helpers
///<summary>
///Currently only used for debugging purposes.
///</summary>
public static void InspectGamePad(int playerNum)
{
GamePadCapabilities gpc = GamePad.GetCapabilities(playerNum);
System.Diagnostics.Debug.WriteLine("inspecting gamepad #" + playerNum);
System.Diagnostics.Debug.WriteLine("\t type: " + gpc.GamePadType);
System.Diagnostics.Debug.WriteLine("\t has left X joystick: " + gpc.HasLeftXThumbStick);
System.Diagnostics.Debug.WriteLine("\t has left Y joystick: " + gpc.HasLeftYThumbStick);
System.Diagnostics.Debug.WriteLine("\t has A button: " + gpc.HasAButton);
System.Diagnostics.Debug.WriteLine("\t has B button: " + gpc.HasBButton);
System.Diagnostics.Debug.WriteLine("\t has X button: " + gpc.HasXButton);
System.Diagnostics.Debug.WriteLine("\t has Y button: " + gpc.HasYButton);
System.Diagnostics.Debug.WriteLine("\t has back button: " + gpc.HasBackButton);
System.Diagnostics.Debug.WriteLine("\t has big button: " + gpc.HasBigButton);
System.Diagnostics.Debug.WriteLine("\t has start button: " + gpc.HasStartButton);
System.Diagnostics.Debug.WriteLine("\t has Dpad Down button: " + gpc.HasDPadDownButton);
System.Diagnostics.Debug.WriteLine("\t has Dpad Left button: " + gpc.HasDPadLeftButton);
System.Diagnostics.Debug.WriteLine("\t has Dpad Right button: " + gpc.HasDPadRightButton);
System.Diagnostics.Debug.WriteLine("\t has Dpad Up button: " + gpc.HasDPadUpButton);
System.Diagnostics.Debug.WriteLine("\t has Left Shoulder button: " + gpc.HasLeftShoulderButton);
System.Diagnostics.Debug.WriteLine("\t has Left Trigger button: " + gpc.HasLeftTrigger);
System.Diagnostics.Debug.WriteLine("\t has Left Stick button: " + gpc.HasLeftStickButton);
System.Diagnostics.Debug.WriteLine("\t has Left vibration motor: " + gpc.HasLeftVibrationMotor);
System.Diagnostics.Debug.WriteLine("\t has Right Shoulder button: " + gpc.HasRightShoulderButton);
System.Diagnostics.Debug.WriteLine("\t has Right Trigger button: " + gpc.HasRightTrigger);
System.Diagnostics.Debug.WriteLine("\t has Right Stick button: " + gpc.HasRightStickButton);
System.Diagnostics.Debug.WriteLine("\t has Right vibration motor: " + gpc.HasRightVibrationMotor);
}
#endregion
}
#region Enums
public struct Keybind
{
public Keys keyboardBinding;
public MouseButton mouseBinding;
public Buttons gamepadBinding;
/// <summary>
/// Whether to use keyboard or mouse on this keybind
/// </summary>
public bool PreferKeyboard;
/// <summary>
/// Whether this keybind only applies on gamepads
/// </summary>
public bool GamepadOnly;
#region Constructors
public Keybind(Keys key)
{
keyboardBinding = key;
mouseBinding = new MouseButton();
gamepadBinding = new Buttons();
GamepadOnly = false;
PreferKeyboard = true;
}
public Keybind(Keys key, Buttons gamepad)
{
keyboardBinding = key;
mouseBinding = new MouseButton();
gamepadBinding = gamepad;
GamepadOnly = false;
PreferKeyboard = true;
}
public Keybind(Buttons gamepad)
{
keyboardBinding = new Keys();
mouseBinding = new MouseButton();
gamepadBinding = gamepad;
GamepadOnly = true;
PreferKeyboard = false;
}
public Keybind(MouseButton mouse, Buttons gamepad)
{
keyboardBinding = new Keys();
mouseBinding = mouse;
gamepadBinding = gamepad;
GamepadOnly = false;
PreferKeyboard = false;
}
public Keybind(Keys keyboard, MouseButton mouse, Buttons gamepad)
{
keyboardBinding = keyboard;
mouseBinding = mouse;
gamepadBinding = gamepad;
GamepadOnly = false;
PreferKeyboard = false;
}
#endregion
}
/// <summary>
/// Defines a mouse button
/// </summary>
public enum MouseButton
{
/// <summary>
/// Left Mouse Button
/// </summary>
Button0,
/// <summary>
/// Right Mouse Button
/// </summary>
Button1,
/// <summary>
/// Middle Mouse Button
/// </summary>
Button2,
/// <summary>
/// Macro 1
/// </summary>
Button3,
/// <summary>
/// Macro 2
/// </summary>
Button4,
}
#endregion
}

This is an InputManager designed to be used in MonoGame framework based projects.

Setup:

In the initialize method, call InputManager.Initialise();, and in the update method call InputManager.Update(); at the start of your update method (this is so that you will have the current frame's inputs when requesting keybind states).

Once that is done, go into the InputManager class and under the Initialise method, you'll find a number of BindKey() calls. To bind a new key, simply call BindKey(string keybind_name, Keybind keybind). The keybind_name field is used to identify the keybind when requesting input states.

Rebinding keys:

To reassign a keybind, simply call InputManager.RebindKey(string keybind_name, Keybind new_keybind);. This method shall reassign a keybind and will assign it with the given Keybind parameter. InputManager also includes a method called CaptureBinding();, which returns a Keybind which represents the current state of keybinds being pressed across all forms of input (keyboard, mouse, controller). You might want to disable mouse controls on a keybind before reassigning it in some cases, or disabling keyboard and mouse input for controller exclusive bindings.

What is a keybind?

A Keybind is a struct that contains 5 fields:

Keys keyboardBinding
MouseButton mouseBinding
Buttons gamepadBinding
bool PreferKeyboard
bool GamepadOnly

keyboardBinding is a Keys object (Microsoft.Xna.Framework.Input.Keys) which represents all keyboard keys that are to be accepted under the keybind mouseBinding is a MouseButton object (Experimental.MouseButton) which represents all mouse buttons that are to be accepted under the keybind. This doesn't include properties such as mouse position or scroll notches. gamepadBinding is a Buttons object (Microsoft.Xna.Framework.Input.Buttons) which represents all controller inputs that are to be accepted under the keybind PreferKeyboard tells the InputManager to ignore controller input if set to true GamepadOnly tells the InputManager to ignore gamepad input if set to true

Example:

protected override void Initialize()
{
	InputManager.Initialise();
}

protected override void Update()
{
	InputManager.Update();
	if (InputManager.Released("misc.screenshot"))
	{
		CaptureScreenshot();
	}
}

Known issues:

Controllers do not work as expected. Currently not a priority to fix.

Reassigning keybinds hasn't been tested yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment