Users interact with your jME3 application with different input devices – the mouse, the keyboard, or a joystick. To respond to inputs we use the inputManager
object in SimpleApplication
.
This is how you add interaction to your game:
Choose one or several key/mouse events for the interaction. We use KeyTrigger
, MouseAxisTrigger
, MouseButtonTrigger
, JoyAxisTrigger
and JoyButtonTrigger
constants from the com.jme3.input.controls
package.
Note: The MouseAxis and JoyAxis triggers go along the X axis (right/left) or Y axis (up/down). These Triggers come with extra booleans for the negative half of the axis (left, down). Remember to write code that listens to the negative (true) and positive (false) axis!
Trigger | Code |
---|---|
Mouse button: Left Click | MouseButtonTrigger(MouseInput.BUTTON_LEFT) |
Mouse button: Right Click | MouseButtonTrigger(MouseInput.BUTTON_RIGHT) |
Mouse button: Middle Click | MouseButtonTrigger(MouseInput.BUTTON_MIDDLE) |
Mouse movement: Right | MouseAxisTrigger(MouseInput.AXIS_X, true) |
Mouse movement: Left | MouseAxisTrigger(MouseInput.AXIS_X, false) |
Mouse movement: Up | MouseAxisTrigger(MouseInput.AXIS_Y, true) |
Mouse movement: Down | MouseAxisTrigger(MouseInput.AXIS_Y, false) |
Mouse wheel: Up | MouseAxisTrigger(MouseInput.AXIS_WHEEL,false) |
Mouse wheel: Down | MouseAxisTrigger(MouseInput.AXIS_WHEEL,true) |
NumPad: 1, 2, 3, … | KeyTrigger(KeyInput.KEY_NUMPAD1) … |
Keyboard: 1, 2 , 3, … | KeyTrigger(KeyInput.KEY_1) … |
Keyboard: A, B, C, … | KeyTrigger(KeyInput.KEY_A) … |
Keyboard: Spacebar | KeyTrigger(KeyInput.KEY_SPACE) |
Keyboard: Shift | KeyTrigger(KeyInput.KEY_RSHIFT), KeyTrigger(KeyInput.KEY_LSHIFT) |
Keyboard: F1, F2, … | KeyTrigger(KeyInput.KEY_F1) … |
Keyboard: Return, Enter | KeyTrigger(KeyInput.KEY_RETURN), KeyTrigger(KeyInput.KEY_NUMPADENTER) |
Keyboard: PageUp, PageDown | KeyTrigger(KeyInput.KEY_PGUP), KeyTrigger(KeyInput.KEY_PGDN) |
Keyboard: Delete, Backspace | KeyTrigger(KeyInput.KEY_BACK), KeyTrigger(KeyInput.KEY_DELETE) |
Keyboard: Escape | KeyTrigger(KeyInput.KEY_ESCAPE) |
Keyboard: Arrows | KeyTrigger(KeyInput.KEY_DOWN), KeyTrigger(KeyInput.KEY_UP) KeyTrigger(KeyInput.KEY_LEFT), KeyTrigger(KeyInput.KEY_RIGHT) |
Joystick Button: | JoyButtonTrigger(0, JoyInput.AXIS_POV_X), JoyButtonTrigger(0, JoyInput.AXIS_POV_Y) ? |
Joystick Movement: Right | JoyAxisTrigger(0, JoyInput.AXIS_POV_X, true) |
Joystick Movement: Left | JoyAxisTrigger(0, JoyInput.AXIS_POV_X, false) |
Joystick Movement: Forward | JoyAxisTrigger(0, JoyInput.AXIS_POV_Z, true) |
Joystick Movement: Backward | JoyAxisTrigger(0, JoyInput.AXIS_POV_Z, false) |
In your IDE, use code completion to quickly look up Trigger literals. In the jMonkeyPlatform for example, press ctrl-space or ctrl-/ after KeyInput.|
to choose from the list of all keys.
When initializing the application, add a Mapping for each Trigger.
Give the mapping a meaningful name. The name should reflect the action, not the button/key (because buttons/keys can change). Here some examples:
inputManager.addMapping("Pause Game", new KeyTrigger(KeyInput.KEY_P)); inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE)); ...
There are cases where you may want to provide more then one trigger for one action. For example, some users prefer the WASD keys to navigate, while others prefer the arrow keys. Add several triggers for one mapping, by separating the Trigger objects with commas:
inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A), new KeyTrigger(KeyInput.KEY_LEFT)); // A and left arrow inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D), new KeyTrigger(KeyInput.KEY_RIGHT)); // D and right arrow ...
The jME3 input manager supports two types of event listeners for inputs: AnalogListener and ActionListener. You can use one or both listeners in the same application. Add one or both of the following code snippets to your main SimpleApplication-based class to activate the listeners.
Note: The two input listeners do not know, and do not care, which actual key was pressed. They only know which named input mapping was triggered.
com.jme3.input.controls.ActionListener
private ActionListener() { public void onAction(String name, boolean keyPressed, float tpf) { /** TODO: test for mapping names and implement actions */ } };
com.jme3.input.controls.AnalogListener
private AnalogListener analogListener = new AnalogListener() { public void onAnalog(String name, float keyPressed, float tpf) { /** TODO: test for mapping names and implement actions */ } };
To activate the mappings, you must register them to a Listener. Write your registration code after the code block where you have added the mappings to the inputManager.
In the following example, you register the "Pause Game" mapping to the actionListener
object, because pausing a game is in "either/or" decision.
inputManager.addListener(actionListener, new String[]{"Pause Game"});
In the following example, you register navigational mappings to the analogListener
object, because walking is a continuous action. Players typically keep the key pressed to express continuity, for example when they want to "walk on" or "accelerate".
inputManager.addListener(analogListener, new String[]{"Left", "Right"});
As you see, you can add several listeners in one String array. You can call the addListener() method more than once, each time with a subset of your list, if that helps you keep you code tidy. Again, the Listeners do not care about actual which keys are configured, you only register named trigger mappings.
You specify the action to be triggered where it says TODO in the Listener code snippets. Typically, you write a series of if/else conditions, testing for all the mapping names, and then calling the respective action.
Make use of the distinction between if
and else if
in this conditional.
if
s. For example, a character can be running forward and to the left.else if
, the the rest of the exclusive tests can be skipped and you save some miliseconds. For example, you either shoot or pick something up.
In the most common case, you want an action to be triggered once, in the moment when the button or key trigger is released. For example, when the player presses a key to open a door, or clicks to pick up an item. For these cases, use an ActionListener and test for && !keyPressed
, like shown in the following example.
private ActionListener() { public void onAction(String name, boolean keyPressed, float tpf) { if (name.equals("Pause Game") && !keyPressed) { // test? isRunning = !isRunning; // action! } if ... } };
The following example shows how you define actions with an AnalogListener. Thiese actions are triggered continuously, as long (intensity value
) as the named key or mouse button is down. Use this listeners for semi-automatic weapons and navigational actions.
private AnalogListener analogListener = new AnalogListener() { public void onAnalog(String name, float value, float tpf) { if (name.equals("Rotate")) { // test? player.rotate(0, value*speed, 0); // action! } if ... } };
It is likely that your players have different keyboard layouts, are used to "reversed" mouse navigation, or prefer different navigational keys than the ones that you defined. You should create an options screen that lets users customize their mouse/key triggers for your mappings. Replace the trigger literals in the inputManager.addMapping()
lines with variables, and load sets of triggers when the game starts.
The abstraction of separating triggers and mappings has the advantage that you can remap triggers easily. Your code only needs to remove and add some trigger mappings. The core of the code (the listeners and actions) remains unchanged.