Install
$ agentstack add skill-kyu-n-gdx-claude-skills-libgdx-controllers ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
libGDX gdx-controllers Extension (2.x)
Reference for the gdx-controllers 2.x extension — controller discovery, input polling, event-driven input, cross-controller mapping, and common patterns.
This covers gdx-controllers 2.x (the current standalone extension). The old 1.x API bundled with libGDX core is deprecated and has a completely different interface. Do NOT use the old API.
1.x vs 2.x — Critical Differences
| | Old 1.x (DEPRECATED) | New 2.x (USE THIS) | |---|---|---| | Gradle group | com.badlogic.gdx:gdx-controllers | com.badlogicgames.gdx-controllers:gdx-controllers-core | | ControllerListener | 8 methods: povMoved, xSliderMoved, ySliderMoved, accelerometerMoved, etc. | 5 methods only: connected, disconnected, buttonDown, buttonUp, axisMoved | | Mapping | None — hardcode button numbers | controller.getMapping() → ControllerMapping | | D-pad | PovDirection enum | Buttons via mapping.buttonDpadUp/Down/Left/Right | | Package | com.badlogic.gdx.controllers | com.badlogic.gdx.controllers (same package, different artifact) |
If you see PovDirection, xSliderMoved, ySliderMoved, or accelerometerMoved — that's the old 1.x API. Stop and switch to 2.x.
Gradle Dependencies
// In gradle.properties:
controllersVersion=2.2.5
// core module:
implementation "com.badlogicgames.gdx-controllers:gdx-controllers-core:$controllersVersion"
// desktop (lwjgl3) module:
implementation "com.badlogicgames.gdx-controllers:gdx-controllers-desktop:$controllersVersion"
// android module:
implementation "com.badlogicgames.gdx-controllers:gdx-controllers-android:$controllersVersion"
// ios module:
implementation "com.badlogicgames.gdx-controllers:gdx-controllers-ios:$controllersVersion"
The group ID is com.badlogicgames.gdx-controllers, NOT com.badlogic.gdx. The old com.badlogic.gdx:gdx-controllers is the deprecated 1.x API.
Controller Discovery
// Get all currently connected controllers
Array controllers = Controllers.getControllers();
// Never assume a controller is available at startup
if (controllers.size > 0) {
Controller controller = controllers.first();
Gdx.app.log("Input", "Found: " + controller.getName());
}
Controllers may connect/disconnect at any time. Always null-check before use and listen for connect/disconnect events.
ControllerMapping — Cross-Controller Button/Axis Codes
NEVER hardcode button or axis numbers. Raw codes differ across controllers and platforms. Always use controller.getMapping():
ControllerMapping mapping = controller.getMapping();
// Buttons — use these named constants
mapping.buttonA // Xbox A / PS Cross
mapping.buttonB // Xbox B / PS Circle
mapping.buttonX // Xbox X / PS Square
mapping.buttonY // Xbox Y / PS Triangle
mapping.buttonL1 // Left bumper / L1
mapping.buttonR1 // Right bumper / R1
mapping.buttonL2 // Left trigger as button / L2
mapping.buttonR2 // Right trigger as button / R2
mapping.buttonLeftStick // Left stick click / L3
mapping.buttonRightStick // Right stick click / R3
mapping.buttonStart // Start / Options
mapping.buttonBack // Back / Select / Share
mapping.buttonDpadUp // D-pad directions
mapping.buttonDpadDown
mapping.buttonDpadLeft
mapping.buttonDpadRight
// Axes — return float from -1.0 to 1.0
mapping.axisLeftX // Left stick horizontal
mapping.axisLeftY // Left stick vertical
mapping.axisRightX // Right stick horizontal
mapping.axisRightY // Right stick vertical
Polling Input
// In render() — poll current state
ControllerMapping m = controller.getMapping();
boolean jumping = controller.getButton(m.buttonA);
float moveX = controller.getAxis(m.axisLeftX);
float moveY = controller.getAxis(m.axisLeftY);
Event-Driven Input
ControllerListener (2.x — 5 methods only)
public interface ControllerListener {
void connected(Controller controller);
void disconnected(Controller controller);
boolean buttonDown(Controller controller, int buttonCode);
boolean buttonUp(Controller controller, int buttonCode);
boolean axisMoved(Controller controller, int axisCode, float value);
}
Only 5 methods. If you see povMoved, xSliderMoved, ySliderMoved, or accelerometerMoved, you're using the old 1.x API.
ControllerAdapter
ControllerAdapter implements ControllerListener with empty defaults — override only what you need:
Controllers.addListener(new ControllerAdapter() {
@Override
public void connected(Controller controller) {
activeController = controller;
}
@Override
public void disconnected(Controller controller) {
if (activeController == controller) activeController = null;
}
@Override
public boolean buttonDown(Controller controller, int buttonCode) {
ControllerMapping m = controller.getMapping();
if (buttonCode == m.buttonA) {
player.jump();
return true;
}
return false;
}
});
Listener Scope
Controllers.addListener()— Global: receives events from ALL controllers, including connect/disconnect.controller.addListener()— Per-controller: receives events only from that specific controller. Does NOT receive connect/disconnect.
Dead Zones
Analog sticks have hardware noise — they rarely rest exactly at 0. Apply a dead zone:
private static final float DEAD_ZONE = 0.2f;
float x = controller.getAxis(mapping.axisLeftX);
float y = controller.getAxis(mapping.axisLeftY);
if (Math.abs(x) controllers = Controllers.getControllers();
if (controllers.size > 0) {
activeController = controllers.first();
}
// Listen for connect/disconnect
Controllers.addListener(new ControllerAdapter() {
@Override
public void connected(Controller controller) {
if (activeController == null) {
activeController = controller;
}
}
@Override
public void disconnected(Controller controller) {
if (activeController == controller) {
activeController = null;
// Fall back to another controller if available
Array remaining = Controllers.getControllers();
if (remaining.size > 0) activeController = remaining.first();
}
}
});
}
public void update(float dt) {
// Always support keyboard as fallback
handleKeyboard(dt);
if (activeController == null) return;
ControllerMapping m = activeController.getMapping();
// Analog stick with dead zone
float moveX = activeController.getAxis(m.axisLeftX);
float moveY = activeController.getAxis(m.axisLeftY);
if (Math.abs(moveX) < DEAD_ZONE) moveX = 0;
if (Math.abs(moveY) < DEAD_ZONE) moveY = 0;
player.x += moveX * SPEED * dt;
player.y += moveY * SPEED * dt;
// Buttons via mapping — works across Xbox, PlayStation, etc.
if (activeController.getButton(m.buttonA)) player.jump();
if (activeController.getButton(m.buttonX)) player.attack();
if (activeController.getButton(m.buttonStart)) togglePause();
}
}
Common Mistakes
- Using the old 1.x controller API — If your
ControllerListenerhaspovMoved,xSliderMoved,ySliderMoved, oraccelerometerMoved, you're using the deprecated 1.x API. The 2.x listener has only 5 methods. Check your Gradle dependency: the group must becom.badlogicgames.gdx-controllers, notcom.badlogic.gdx. - Hardcoding button/axis numbers —
controller.getButton(0)orcontroller.getAxis(1)will break across controllers and platforms. Always usecontroller.getMapping().buttonA,.axisLeftX, etc. - Assuming a controller is always connected — Controllers can be disconnected at any time. Null-check before use, listen for disconnect events, and always support keyboard as fallback.
- No dead zone on analog sticks — Without a dead zone (0.15–0.25), characters drift from hardware noise on idle sticks.
- Building a custom mapping system —
ControllerMappingalready abstracts Xbox/PlayStation/generic differences. Don't build your own mapping layer with hardcoded per-controller button tables. - Using invented constants —
ControllerButton.A,ControllerAxis.LeftX,Axes.leftXdo NOT exist. The correct API iscontroller.getMapping().buttonA,controller.getMapping().axisLeftX. - Only listening per-controller —
controller.addListener()does not receive connect/disconnect events. UseControllers.addListener()for global events including hotplug.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: kyu-n
- Source: kyu-n/gdx-claude-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.