I’ve been looking for something gamepad related like a getAButton() method that, when called, would return me a Trigger (JoyAxisTrigger or JoyButtonTrigger) corresponding to the button located at the position of the A button on a Xbox gamepad – which would be the cross button on a PlayStation controller or the B button on a SNES controller. As many of you know, this is exactly what SDL GameControllerDB is meant for.
I couldn’t find anything like that on jME, so I created my own class. It works, but I have that itchy feeling that I’m reinventing the wheel, so I decided to come here and ask. Is there anything like that in the engine?
This is the class I created:
public class JoystickInput {
private final Logger logger = Logger.getLogger(JoystickInput.class.getName());
private final int id;
private final String guid;
private final Map<String, Trigger> joyMapping;
public JoystickInput(final int id) {
this.id = id;
this.guid = GLFW.glfwGetJoystickGUID(id);
this.joyMapping = new HashMap<>();
}
public int getId() {
return id;
}
public String getGUID() {
return guid;
}
public Trigger getA() {
return joyMapping.get("a");
}
public Trigger getB() {
return joyMapping.get("b");
}
public Trigger getX() {
return joyMapping.get("x");
}
public Trigger getY() {
return joyMapping.get("y");
}
public Trigger getCross() {
return getA();
}
public Trigger getCircle() {
return getB();
}
public Trigger getSquare() {
return getX();
}
public Trigger getTriangle() {
return getY();
}
public Trigger getBackButton() {
return joyMapping.get("back");
}
public Trigger getSelect() {
return getBackButton();
}
public Trigger getStart() {
return joyMapping.get("start");
}
public Trigger getHomeButton() {
return joyMapping.get("guide");
}
public Trigger getPSButton() {
return getHomeButton();
}
public Trigger getLeftShoulder() {
return joyMapping.get("leftshoulder");
}
public Trigger getRightShoulder() {
return joyMapping.get("rightshoulder");
}
public Trigger getL1() {
return getLeftShoulder();
}
public Trigger getR1() {
return getRightShoulder();
}
public Trigger getL2() {
return getLeftTrigger();
}
public Trigger getR2() {
return getRightTrigger();
}
public Trigger getLeftStickButton() {
return joyMapping.get("leftstick");
}
public Trigger getRightStickButton() {
return joyMapping.get("rightstick");
}
public Trigger getL3() {
return getLeftStickButton();
}
public Trigger getR3() {
return getRightStickButton();
}
public Trigger getXAxis() {
return joyMapping.get("leftx");
}
public Trigger getXAxisInv() {
return joyMapping.get("leftx~");
}
public Trigger getYAxis() {
return joyMapping.get("lefty");
}
public Trigger getYAxisInv() {
return joyMapping.get("lefty~");
}
public Trigger getRightStickXAxis() {
return joyMapping.get("rightx");
}
public Trigger getRightStickXAxisInv() {
return joyMapping.get("rightx~");
}
public Trigger getRightStickYAxis() {
return joyMapping.get("righty");
}
public Trigger getRightStickYAxisInv() {
return joyMapping.get("righty~");
}
public Trigger getLeftTrigger() {
return joyMapping.get("lefttrigger");
}
public Trigger getLeftTriggerInv() {
return joyMapping.get("lefttrigger~");
}
public Trigger getRightTrigger() {
return joyMapping.get("righttrigger");
}
public Trigger getRightTriggerInv() {
return joyMapping.get("righttrigger~");
}
public Trigger getDpadUp() {
return joyMapping.get("dpup");
}
public Trigger getDpadRight() {
return joyMapping.get("dpright");
}
public Trigger getDpadDown() {
return joyMapping.get("dpdown");
}
public Trigger getDpadLeft() {
return joyMapping.get("dpleft");
}
/**
* Initializes a joystick based on SDL GameControllerDB, where:<br>
* - Data are comma separated.<br>
* - The first value is the joystick model GUID number.<br>
* - The second value is the joystick model name.<br>
* - The other values are in the form <field>:<value> and describe the joystick mapping.<br>
* * <value> can be <b>aN</b> for axis, <b>bN</b> for button or <b>hN.N</b> for hat
* (D-pad). The axis value may also include a <b>~ (aN~)</b> which represents an inversion modifier.<br>
*
* @param inputManager the application input manager.
* @see <a href="https://github.com/gabomdq/SDL_GameControllerDB" target="_top">SDL GameControllerDB</a>
* @see <a href="https://www.glfw.org/docs/3.3/input_guide.html#gamepad_mapping" target="_top">GLFW Gamepad mappings</a>
*/
public boolean initialize(final InputManager inputManager) {
if (id >= inputManager.getJoysticks().length) {
logger.log(Level.WARNING, "Joystick not connected: " + id);
return false;
}
final Joystick joystick = inputManager.getJoysticks()[id];
logger.log(Level.INFO,
"Initializing joystick " + id + "\n * Name: " + joystick.getName() + "\n * GUID: " + guid);
String line = "";
try (final InputStream stream = JoystickInput.class.getResourceAsStream("/gamecontrollerdb.txt")) {
final Scanner scanner = new Scanner(Objects.requireNonNull(stream));
while (scanner.hasNext() && !line.startsWith(guid)) {
line = scanner.nextLine();
}
} catch (final IOException e) {
logger.log(Level.SEVERE, "One or more joysticks cannot be initialized");
return false;
}
if (!line.startsWith(guid)) {
logger.log(Level.WARNING, "Joystick {0} cannot be identified or is not a gamepad controller", id);
return false;
}
final String[] joymap = line.split(",");
for (int i = 2; i < joymap.length; i++) {
final String[] entry = joymap[i].split(":");
if (!entry[0].equals("platform")) {
final Trigger trigger;
final int value = Integer.parseInt(entry[1].replaceAll("\\D+", ""));
final boolean negative = entry[1].contains("~");
if (entry[1].contains("a")) {
trigger = new JoyAxisTrigger(id, value, negative);
joyMapping.put(entry[0] + "~", new JoyAxisTrigger(id, value, !negative));
} else if (entry[1].contains("b")) {
trigger = new JoyButtonTrigger(id, value);
} else {
trigger = switch (entry[1]) {
case "h0.1" -> new JoyButtonTrigger(id, GLFW.GLFW_GAMEPAD_BUTTON_DPAD_UP);
case "h0.2" -> new JoyButtonTrigger(id, GLFW.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT);
case "h0.4" -> new JoyButtonTrigger(id, GLFW.GLFW_GAMEPAD_BUTTON_DPAD_DOWN);
case "h0.8" -> new JoyButtonTrigger(id, GLFW.GLFW_GAMEPAD_BUTTON_DPAD_LEFT);
default -> null;
};
}
joyMapping.put(entry[0], trigger);
}
}
return true;
}
}
Usage: create an instance of JoystickInput
passing the joystick id in the constructor, then make a call to initialize()
with your application’s inputManager
. Use the get
methods to retrieve the corresponding trigger.