Skip to content
Benjamin Schulte edited this page Feb 4, 2021 · 7 revisions

The goal of this extension is to provide the following functionality:

  • Enumerate connected controllers
  • Support for buttons, axes, vibration and more typically used functionality on game controllers
  • Listen for controller events globally or per controller
  • Poll controller state

Enumerating Controllers

One or more controllers might be attached to your PC/device. The Controllers class has methods to query for currently connected devices:

for (Controller controller : Controllers.getControllers()) {
    Gdx.app.log(TAG, controller.getName());
}

The order of controllers in the Array returned by Controllers.getControllers() is usually the order in which the controllers were connected. However, you should not rely on this. See the section on (dis-)connecting devices below.

All methods of the Controllers class must be executed on the rendering thread, that is, in any of the methods of ApplicationListener.

Retrieving the currently used controller

(version 2.2.0+) If you implement a single-player game, you might only be interested in getting the Controller instance the player currently uses. Controllers.getCurrent() will return you the Controller instance the player is currently using or used most recently. This reference will change when the player switches the controller, and it might be null if no controller is available or the player's controller disconnected.

Polling Controller State

Once you have a Controller instance, you can ask it for its current state. A controller can have different components:

  • Buttons: this includes your usual ‘X’, ‘Y’ buttons as well as the d-pad on some controllers. A button can be pressed or released.
  • Axes: usually provided via analogue sticks. An axes has a value between -1 and 1, with 0 being the neutral center position, 1 being right for horizontal axis, down for vertical axis. Note that some platforms and controllers may report some button combinations (d-pad) as axis or vice versa.

Polling for the state of a specific component is pretty simple:

boolean buttonPressed = controller.getButton(buttonCode);
float axisValue = controller.getAxis(axisCode);
...

Event based Controller Input

When polling a controller, you might lose some input events. For some tasks, event based input is also more suited than polling based input. We hence provide you a way to register listeners to receive controller events, either globally for all controllers, or for a specific controller. The interface you have to implement to listen for controller events is called ControllerListener

public interface ControllerListener {
	public void connected(Controller controller);
	public void disconnected(Controller controller);
	public boolean buttonDown (Controller controller, int buttonCode);
	public boolean buttonUp (Controller controller, int buttonCode);
	public boolean axisMoved (Controller controller, int axisCode, float value);
}

Each method receives the Controller instance for which the event was triggered. The first two methods are called when a controller is connected or disconnected. The next two methods report button press and release events. Both receive a buttonCode identifying the button that was pressed or released. The axisMoved method is called when an axis value changed.

A ControllerListener can be either added to Controllers, in which case it will listen for events from all controllers, or to a specific Controller, in which case it will only receive events from that controller:

Controllers.addListener(listener); // receives events from all controllers
controller.addListener(listener); // receives events only from this controller

If you don’t want to implement all these methods, but only a select few, you can subclass ControllerAdapter.

controller.addListener(new ControllerAdapter() {
   @Override
   public void connected(Controller controller) {
      ...
   }
 
   public void disconnected(Controller controller) {
      ...
   }
});

You might have noticed that some of the listener methods return a boolean. This is used if multiple listeners were registered globally or with a specific controller. If such a method returns false, the event will be handed to the next listener. If the method returns true, the event will not be propagated any further.

All listener methods will be called on the rendering thread, just like in case of a normal InputProcessor.

Mappings and Codes

Button and axis codes are not standardized in general, but attempts are made to deal with that. You can get a platform-specific standard mapping by calling getMapping(). These mappings are, depending of the platform, either guaranteed - or only correct in most cases. See Features for more information. If the mappings are not guaranteed, you should be prepared that mappings might slightly differ, and that it might even happen that a single button press raises more than just one listener event.

So we have two options when dealing with controllers:

  • Match button and axis codes against the mapping retrieved with getMapping()
  • Let the player configure the controller bindings

Use provided standard mapping

You can query the state of Button A independant of the platform and connected controller type with the following code:

 controller.getButton(controller.getMapping().buttonA));

And you can query the state of the x-axis of the left analogue stick with

 controller.getAxis(controller.getMapping().axisLeftX));

However, you will get into trouble if you are on a platform that doesn't guarantee correct mappings, or if the connected controller does not provide some features like analogue sticks. For example the iBuffalo Classic has has a D-Pad only, and this D-Pad is mapped on left stick on some platforms, on some it is reported as D-Pad buttons.

On the contrary, some users might have a analogue stick, but would prefer to play your game on the D-Pad. That's why even with guaranteed mappings, a configurable button mapping is the only solution safe for all users.

Configurable button mappings

In the end its best to let the user decide how he wants to use the gamepad with your game. Writing a simple config dialog that runs through actions and asks the user to press a button is easy to do. You can then save those mappings to a file, in combination with the controller name, and reload and reuse them for the next session of that user.

A ready-made library for handling all these cases on top of gdx-controllers can be found at the gdx-controllerutils project.

Controller (Dis-)connects

If your game supports gamepads, make sure to handle device disconnects and connects! E.g. if a controller disconnects during a game, pause the game and ask the user to reconnect the controller. Note that you will get a brand new Controller instance in that case, so make sure to wire up any listeners correctly. The old controller instance will be reported as disconnected, you can check with isConnected().

Tests & Demos

A test project is included with this library.

Other features

See Features page to check platform compatibility with other provided features.

Vibration

boolean canVibrate();
boolean isVibrating();
void startVibration(int milliseconds, float strength);
void cancelVibration();

Self explanatory.

Connection state

String getUniqueId();

If more than one controller of the same type is connected, this unique ID helps you identifying a certain controller instance.

boolean isConnected();

If you hold references to a controller in your game, you might want to know if this controller is already disconnected without having to look it up in the controllers array.

Query available features

int getMinButtonIndex();
int getMaxButtonIndex();
int getAxisCount();

Player Index

Some controllers can show the player index (like the Wii Remotes or Xbox controllers can).

boolean supportsPlayerIndex();
int getPlayerIndex();
void setPlayerIndex(int index);

Power level

Some platforms can report a controller's power level and connection status.

ControllerPowerLevel getPowerLevel()