🎮 How to use input4j in your Java Game
A practical guide to adding gamepad support to your Java game
Table of Contents
Setting up input4j
First, add input a dependency to your project:
For Gradle projects:
Gradle
dependencies {
implementation 'de.gurkenlabs:input4j:1.1.1'
}
For Maven projects:
Maven
<dependency>
<groupId>de.gurkenlabs</groupId>
<artifactId>input4j</artifactId>
<version>1.1.1</version>
</dependency>
Device Initialization
Initialize the input system when your game starts:
Java
import de.gurkenlabs.input4j.InputDevices;
import de.gurkenlabs.input4j.InputDevice;
public class Game {
private List<InputDevice> controllers;
public void init() {
// Initialize the input system
var deviceList = InputDevices.init();
// Get all connected controllers
controllers = deviceList.getAll();
// Or get a specific controller by index
// InputDevice controller = controllers.get(0);
System.out.println("Found " + controllers.size() + " controllers");
}
}
Polling vs Events
input4j supports two input handling modes:
Event-Based (Recommended)
Register callbacks for button presses and axis changes. Better for event-driven architectures.
Polling
Query current input state each frame. Better for game loops with fixed update cycles.
Event-Based Example
// Event-based: Register callbacks
device.onButtonPressed(XInput.Button.A, () -> {
player.jump();
});
device.onButtonReleased(XInput.Button.B, () -> {
player.attack();
});
device.onAxisChanged(XInput.Axis.LEFT_X, value -> {
player.setHorizontalMovement(value);
});
Polling Example
// Polling: Query in game loop
public void update(float deltaTime) {
for (InputDevice device : controllers) {
// Check button state
if (device.isPressed(XInput.Button.A)) {
player.jump();
}
// Get axis value (-1.0 to 1.0)
float leftX = device.getAxisValue(XInput.Axis.LEFT_X);
float leftY = device.getAxisValue(XInput.Axis.LEFT_Y);
player.move(leftX, leftY);
}
}
Integration with Game Loop
Here's a complete example of integrating input4j with a typical game loop:
Java
public class Game {
private InputDevice controller;
public void init() {
var devices = InputDevices.init();
this.controller = devices.getAll().stream()
.findFirst()
.orElse(null);
if (controller != null) {
// Use event-based for menu navigation
controller.onButtonPressed(XInput.Button.START, () -> {
startGame();
});
controller.onButtonPressed(XInput.Button.BACK, () -> {
openMenu();
});
}
}
public void update(float deltaTime) {
if (controller == null) return;
// Polling for movement during gameplay
float moveX = controller.getAxisValue(XInput.Axis.LEFT_X);
float moveY = controller.getAxisValue(XInput.Axis.LEFT_Y);
// Apply deadzone
if (Math.abs(moveX) < 0.1f) moveX = 0;
if (Math.abs(moveY) < 0.1f) moveY = 0;
player.setVelocity(moveX * SPEED, moveY * SPEED);
}
}
Controller Mapping
Different controllers map to different button definitions:
| Controller | Button Set | Notes |
|---|---|---|
| Xbox | XInput.Button | A, B, X, Y, LB, RB, START, BACK, etc. |
| PlayStation | DualShock4.Button | CROSS, CIRCLE, SQUARE, TRIANGLE, etc. |
| Generic | Button (0-127) | Raw button indices |
Force Feedback / Rumble
Add haptic feedback to enhance game feel:
Java
// Set vibration intensity (0.0 to 1.0)
device.setVibration(0.5f, 0.5f); // Left motor, Right motor
// Rumble on hit
device.setVibration(1.0f, 0.0f);
Thread.sleep(100);
device.setVibration(0.0f, 0.0f);
// Or use try-with-resources for auto-cleanup
try (var devices = InputDevices.init()) {
for (var device : devices.getAll()) {
device.setVibration(0.8f, 0.8f);
}
}