Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / game

XInputium — Getting Input from an XInput Controller

5.00/5 (6 votes)
10 Dec 2022MIT6 min read 6.5K  
Introduction to XInputium — a full-featured open-source library for XInput integration in .NET games or applications.
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
C#
// Create an 'XGamepad' instance to use across the application.
XGamepad gamepad = new();

// Register an event handler, just for testing. It fires whenever a button is pressed.
// You would usually put this inside your class constructor.
gamepad.ButtonPressed += (s, e) => Debug.WriteLine
        ($"Hello, universe! Button {e.Button} was pressed.");

// Call this on every app/game frame. This will notify XInputium that 
// it can now perform one more iteration on the input loop. Any input 
// events will be fired now.
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:

C#
XGamepad gamepad = new();

// Subscribe a dynamic event that fires when button A is held for 250ms.
var buttonAHoldEvent = gamepad.RegisterButtonHoldEvent(
    XButtons.A, TimeSpan.FromMilliseconds(250), ButtonHeld);

// Call this on every game/application frame.
gamepad.Update();

// Optionally, unregister the event, later on, when you don't need it anymore.
gamepad.UnregisterInputEvent(buttonAHoldEvent);

// This method will handle the event, just like it would with a regular .NET event.
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.

XInputium Preview

History

  • 10th December, 2022: Initial article release

License

This article, along with any associated source code and files, is licensed under The MIT License