Gamepad Keymapping and type of the gamepad with Lemur

How to use Lemur’s support for multiple controllers.

As well I tried to figure out how to get the controller name so I could implement a customized key name as on my nacon controller it is a “1” where it is “B” on the Nintendo controller and an “X” on the PS4 controller.

At the moment I just map Keys and joystick buttons to “something” I don’t see the joystick… But I might be just blind and it is directly in front of me.

here is an example (my current game does read it from a file and is a bit less straight forward so I took an older game, but the same principle). I can not see in this code where it knows which gamepad, it looks to me that this is only for one and if there are two it just picks one of the two…

package ch.artificials.bg.view.states.player;

import com.jme3.input.KeyInput;
import com.simsilica.lemur.input.Axis;
import com.simsilica.lemur.input.Button;
import com.simsilica.lemur.input.FunctionId;
import com.simsilica.lemur.input.InputMapper;
import com.simsilica.lemur.input.InputState;

public class PlayerMovementFunctions {

    public static final String G_MOVEMENT = "Movement";
    public static final String G_ACTION = "Action";

    public static final FunctionId F_X_ROTATE_MOUSE = new FunctionId(G_MOVEMENT, "RotateMouse");
    public static final FunctionId F_X_ROTATE_KEYBOARD = new FunctionId(G_MOVEMENT, "RotateKeyboard");
    public static final FunctionId F_X_ROTATE_CONTROLER = new FunctionId(G_MOVEMENT, "RotateControler");

    public static final FunctionId F_THRUST = new FunctionId(G_MOVEMENT, "Thrust");
    public static final FunctionId F_STRAFE = new FunctionId(G_MOVEMENT, "Strafe");
    public static final FunctionId F_BOOST = new FunctionId(G_MOVEMENT, "Boost");

    public static final FunctionId F_SHOOT = new FunctionId(G_ACTION, "Shoot");
    public static final FunctionId F_LAUNCH = new FunctionId(G_ACTION, "Launch");
    public static final FunctionId F_TAKE_SCREENSHOT = new FunctionId(G_ACTION, "Take Screenshot");

    public static void initializeDefaultMappings(InputMapper inputMapper) {

        if (!inputMapper.hasMappings(F_THRUST)) {
            inputMapper.map(F_THRUST, InputState.Negative, Axis.JOYSTICK_LEFT_Y);
            inputMapper.map(F_THRUST, KeyInput.KEY_W);
            inputMapper.map(F_THRUST, InputState.Negative, KeyInput.KEY_S);
        }

        if (!inputMapper.hasMappings(F_STRAFE)) {
            // Strafing is setup similar to move.
            inputMapper.map(F_STRAFE, Axis.JOYSTICK_LEFT_X);
            inputMapper.map(F_STRAFE, KeyInput.KEY_D);
            inputMapper.map(F_STRAFE, InputState.Negative, KeyInput.KEY_A);
        }

        if (!inputMapper.hasMappings(F_X_ROTATE_MOUSE)) {
            inputMapper.map(F_X_ROTATE_MOUSE, Axis.MOUSE_X);
        }

        if (!inputMapper.hasMappings(F_X_ROTATE_KEYBOARD)) {
            inputMapper.map(F_X_ROTATE_KEYBOARD, KeyInput.KEY_RIGHT);
            inputMapper.map(F_X_ROTATE_KEYBOARD, InputState.Negative, KeyInput.KEY_LEFT);
        }
        
        if (!inputMapper.hasMappings(F_X_ROTATE_CONTROLER)) {
            inputMapper.map(F_X_ROTATE_CONTROLER, Axis.JOYSTICK_RIGHT_X);
        }

        if (!inputMapper.hasMappings(F_SHOOT)) {
            inputMapper.map(F_SHOOT, Button.JOYSTICK_RIGHT2);
            inputMapper.map(F_SHOOT, KeyInput.KEY_SPACE);
            inputMapper.map(F_SHOOT, Button.MOUSE_BUTTON1);
        }

        if (!inputMapper.hasMappings(F_LAUNCH)) {
            inputMapper.map(F_LAUNCH, Button.JOYSTICK_LEFT2);
            inputMapper.map(F_LAUNCH, KeyInput.KEY_UP);
            inputMapper.map(F_LAUNCH, Button.MOUSE_BUTTON2);
        }

        if (!inputMapper.hasMappings(F_TAKE_SCREENSHOT)) {
            inputMapper.map(F_TAKE_SCREENSHOT, KeyInput.KEY_P);
        }

    }

}

And I ask because the testers for my demo stuck mainly because of issues with the gamepad. For example, if I run my gamepad with XInput instead DirectInput8 the R1,2,3 and L1,2,3 are not functional (they probably are axis there). And the player has not a clue what a jostick_button1 is :slight_smile: he wants to read X or B or 1 depending on the controller.

I guess it just listens to all joysticks and looks if there is a mapping. But how can I for example detect which one is in use (games I know to do this by letting the player press start to connect) and get the type of the controller? If I know the type I could provide a proper translation of the button…

Additional Note:
I found the Lemur mapping which maps all the buttons to more straight forward names on a gamepad. And I saw Lemur does this for all possible joystick. But let’s say I have plugged in two controller, which one will Lemur / Jme take? I cant see that. Both together? One of them?
And is there a better way to handle different kind of gamepads and is it even possible to know which type of controller is connected with Lemur?

I’m currently looking into GitHub - williamahartman/Jamepad: A better way to use gamepads in Java as well to get the gamepad name. Wonder if somebody used this already successfully or if Lemur can do similar things and I just don’t know. I obviously prefer a Lemur approach.

Something related to game controllers was discussed recently. I think it might be interesting for you.

As to this question specifically, Lemur (and JME) allow multiple controllers to be plugged in at the same time.

Here is a small game example that allows two side-by-side players to fly little ships around and shoot at each other:

…one gamepad each.

As to your other questions, is your main motivation to be able to show the player “Press B to continue” type of prompts? If so, that’s not something that either API handles well. And I know that at least for lwjgl2 that the jinput API doesn’t provide this information either… and it varies from gamepad to gamepad even for ones that provide the same “gamepad name”. I think perhaps that API that lwjgl3 uses at least provides the vendor ID but I don’t have much familiarity with that.

1 Like

Yes, it’s a bit of both. Let’s say there are two controllers plugged in and you take one of them i.e. an Xbox gamepad and start playing. At this point, it would be nice to know joystick X is in use and my translation of the buttons is for Xbox controller for all my in-game hints.

As well, like in this other thread, jme is not good with plugin controller at runtime and spams the error.log and also makes that controller unusable (also when you switch mode on that Xbox gamepad D/X) and you basically are forced to shut down the game and restart it.

So basically the built-in logic does not provide me enough info if I got you correctly?

I would love to provide solid gamepad support for the guys who test my game. Most of them are anyway game devs and can work around the lack of proper gamepad button names. But normal games probably expect a bit more comfort. A saw there are others with similar problems and possible solutions. I go and play a bit with the possibilities laying around luckily it is java with a lot of libs :slight_smile:

        inputMapper.map(F_TEST2, InputDevice.JOYSTICK1.button(Button.JOYSTICK_SELECT));

Ok this is the way you can specify not only the button but also the joystick…

1 Like

This is one of BigBanana’s goal! With @ia97lies 's use case, I think the only limitation is that it supports only one controller (shouldn’t be hard to add support to multiple controller though).
Jamepad looks like it’s basically adding libgdx dependencies… but why adding new dependencies when GLFW already provides the functionality?

Jamepad can handle hot plug of controllers. Jme I guess relays GLFW and can not handle hot pluged gamepads. Even worse if I press the D/X on my cheap Windows gamepad it reacts the same like unplugging the gamepad and there is no way out but restart the game.
But I might be wrong and your BigBanana project can handle this? I will give it a try as well as currently I’m just look around for any possible solution.
Your right the jamepad not only draw in new dependencies but as well needs a usb dll that I would need to put into my package. But it’s a really simple API and I kinda like it.

It’s based on GLFW so theoretically yes, but my library ignores this information and just used the first…
https://www.glfw.org/docs/latest/input_guide.html#joystick

The API is simple as well, the tricky part is shoehorning it into InputMapper. This is why I’m using a forked Lemur :frowning:

Looking at this is still on my to-do list. Just a bunch of things ahead of it, unfortunately.

1 Like

You mean jME’s gamepads connect/disconnect API does not work?

Idk, I just use the gamepad with the Lemur example and if I press D/X (DirectInput8 / XInput) I end up getting this error (there is another thread for this):

Mar 24, 2022 8:02:55 PM net.java.games.input.ControllerEnvironment log
INFO: Failed to poll device: Failed to poll device (8007000c)

And the controller is not working anymore at all. Same error happens when you unplug the controller. Even when I unplug my headset (also USB) I get the error spam. The remaining controllers still works.

But I read something about GLFW3 and I have not a clue which one is in place for me. I use jme 3.3.2 there is noting in my gradle.build about glfw…

jme3-lwjgl3-3.3.2-stable depends on lwjgl-3.2.3, which includes a 3.4.0 pre-release of GLFW:

1 Like