Learn how to get input from an XInput controller, using XInputium library, in .NET. In this article, we will have a first glimpse at XInputium.
Introduction
When the task is getting input from an XInput compatible device on your .NET application or game, there are several options at your disposal. Commonly, the easiest option, is using a library that provides you access to XInput controllers. XInputium
is a .NET library specialized on XInput, which aims to make it as easy as possible to consume, while still providing powerful features. In this article, you'll learn how to use XInputium
to bring controller support into your game or application.
What is XInputium
XInputium
is a free, open-source library for .NET, that provides applications with access to XInput compatible devices. On its repository, at GitHub, you can see it in action, on a demo application (XInputium
Preview), which allows users to diagnose XInput controllers and test some of XInputium
's features.
There are several libraries out there that provide XInput related functionality to .NET applications. What makes XInputium
any different? Let me answer this question, by mentioning the specific problem: most libraries that provide XInput functionality are intended for game development, thus being called from within the game loop and leaving most input handling to you. For instance, they allow you to get the current state of the controller, but it's up to you what you make of that state, you need to write your own code to detect button presses, button holds, joystick movements, etc.
XInputium
was created to solve that problem. Its goal is for developers to write less code and let the library handle the majority of the input related logic. For example, it allows you to detect specific input events, like when a specific button is held for a certain amount of time, or when a joystick is moved at a certain speed in a specific direction. XInputium
provides ways to automate most of the input logic you would need in a game or application.
Among its features, XInputium
is also ready to be used in an XAML application. Its properties are bindable to XAML, making it simpler to bring out-of-the-box XInput support to applications, instead of just games.
To achieve its objective, XInputium
uses the concept of an internal input loop to perform most of its input handling. You just need to call a method on each application or game frame, from your code; this allows XInputium
to iterate though its internal input loop and gives it the opportunity to measure time. The internal input loop enables XInputium
to compare input between input device states and to provide event based input handling. It abstracts much of the common input logic operations from consumers, although you can still use it with a more low level approach if you wish.
Getting Started with XInputium
Suppose you already have an application/game project, and you need to allow your users to interact with your application/game using an XInput compatible controller.
Install XInputium on Your Project
The first thing you need to do, naturally, is adding XInputium NuGet package to your project:
Install-Package XInputium
Create your "Hello world!"
Now that your project is set up, let's go to the code. Start with the typical "Hello world!
". The following example writes a message into the debug window every time the user presses any button on the controller. Evidently, you need a connected XInput compatible controller for this to work.
"Hello world!" Example
XGamepad gamepad = new();
gamepad.ButtonPressed += (s, e) => Debug.WriteLine
($"Hello, universe! Button {e.Button} was pressed.");
gamepad.Update();
In the previous example, the XGamepad
instance represents a gamepad (or game controller), and when it is instantiated, it uses the first connected device available on the system, if there is any. The XGamepad
constructor has several overloads, that allow you to specify a device, or even specify no device at all.
The gamepad.Update()
method is where everything happens. When you call this method, the state of the gamepad
instance is updated with fresh information from the physical device, its properties are updated, and the appropriate events are triggered. In a typical application or game, you would call this method once per every graphical frame.
Using Dynamic Events
The "Hello world!
" example above fires an event whenever a button is pressed. You can determine what button was pressed using the event arguments parameter (i.e., e.Button
). That is nice, but what about events that fire when specific things happen, like when a specific button is held for a certain amount of time? Native .NET events are written in stone — they don't allow us to specify how an event is fired and when; even worse, they don't allow us to create new events on demand. To address this, XInputium
provides dynamic events — events that participate in the input loop, and fire only when specified conditions are met.
XInputium
's dynamic events are object instances that you register in an XGamepad
instance. These objects derive from the InputEvent
class. There are several classes that derive from InputEvent
, each representing a type of dynamic event you can register. For example, DigitalButtonInputEvent
fires whenever a specified button is either pressed, released, or held, accordingly to your criteria. There are other dynamic events that are adequate for other tasks, like firing repeatedly while the user holds a button (useful for menus and lists), or doing something accordingly to a lambda function, etc.
Let's see how you could register a dynamic event that fires whenever the user holds button A for 250 milliseconds:
XGamepad gamepad = new();
var buttonAHoldEvent = gamepad.RegisterButtonHoldEvent(
XButtons.A, TimeSpan.FromMilliseconds(250), ButtonHeld);
gamepad.Update();
gamepad.UnregisterInputEvent(buttonAHoldEvent);
void ButtonHeld(object? sender, DigitalButtonInputEventArgs<XInputButton> e)
{
Debug.WriteLine($"Button {e.Button} was held.");
}
In the example we just saw, gamepad.RegisterButtonHoldEvent()
method is called to register the dynamic event. This method creates a new instance of DigitalButtonInputEvent
class, registers it in the input loop, adds the ButtonHeld()
method as an event handler, and returns the created event instance (so you can change it later, use the instance to unregister the event or add more handlers to it). XGamepad
class has several methods for registering specific dynamic events, as well as methods to register instances of dynamic events you already have instantiated.
Dynamic events allow for very nice things to be made. For instance, the ActivationInputEvent
class is extremely versatile, allowing you to specify a callback function that is called to determine when the event should fire, and can be set with several delays that instruct when and how the event should fire. This event allows for things like indicating when the joystick or trigger is moved while ignoring very short movements, or making a game character jump higher or lower depending on by how much time the user held the jump button, or firing only when a set of buttons are pressed together.
Documentation and Further Reading
XInputium
has many features. Because this article is just an introduction, we've seen the very basics about how it can be used, but you can find more information and examples on its documentation, in the wiki section of XInputium project on GitHub.
XInputium Preview
You can also take a look at XInputium
Preview's source code, in the same GitHub project page. This is the demo application that demonstrates some of the XInputium
's features. It is a WPF application, that binds its XAML directly to XInputium
objects, leaving most of the input logic to the XInputium
library. You can also run this application to have an idea about what XInputium
can do.
History
- 10th December, 2022: Initial article release