input_handling.adoc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. = Input Handling
  2. :author:
  3. :revnumber:
  4. :revdate: 2016/03/17 20:48
  5. :keywords: keyinput, input, documentation
  6. :relfileprefix: ../../
  7. :imagesdir: ../..
  8. ifdef::env-github,env-browser[:outfilesuffix: .adoc]
  9. 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`.
  10. This is how you add interaction to your game:
  11. . For each action, choose the trigger(s) (a key or mouse click etc)
  12. . For each action, add a trigger mapping to the inputManager
  13. . Create at least one listener in SimpleApplication
  14. . For each action, register its mappings to a listener
  15. . Implement each action in the listener
  16. == Code Samples
  17. * link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/input/TestControls.java[TestControls.java]
  18. * link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/input/TestJoystick.java[TestJoystick.java]
  19. == 1. Choose Trigger
  20. 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.
  21. [NOTE]
  22. ====
  23. 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!
  24. ====
  25. [cols="2", options="header"]
  26. |===
  27. a| Trigger
  28. a| Code
  29. a| Mouse button: Left Click
  30. a| MouseButtonTrigger(MouseInput.BUTTON_LEFT)
  31. a| Mouse button: Right Click
  32. a| MouseButtonTrigger(MouseInput.BUTTON_RIGHT)
  33. a| Mouse button: Middle Click
  34. a| MouseButtonTrigger(MouseInput.BUTTON_MIDDLE)
  35. a| Mouse movement: Right
  36. a| MouseAxisTrigger(MouseInput.AXIS_X, true)
  37. a| Mouse movement: Left
  38. a| MouseAxisTrigger(MouseInput.AXIS_X, false)
  39. a| Mouse movement: Up
  40. a| MouseAxisTrigger(MouseInput.AXIS_Y, true)
  41. a| Mouse movement: Down
  42. a| MouseAxisTrigger(MouseInput.AXIS_Y, false)
  43. a| Mouse wheel: Up
  44. a| MouseAxisTrigger(MouseInput.AXIS_WHEEL,false)
  45. a| Mouse wheel: Down
  46. a| MouseAxisTrigger(MouseInput.AXIS_WHEEL,true)
  47. a| NumPad: 1, 2, 3, …
  48. a| KeyTrigger(KeyInput.KEY_NUMPAD1) …
  49. a| Keyboard: 1, 2 , 3, …
  50. a| KeyTrigger(KeyInput.KEY_1) …
  51. a| Keyboard: A, B, C, …
  52. a| KeyTrigger(KeyInput.KEY_A) …
  53. a| Keyboard: Spacebar
  54. a| KeyTrigger(KeyInput.KEY_SPACE)
  55. a| Keyboard: Shift
  56. a| KeyTrigger(KeyInput.KEY_RSHIFT), +
  57. KeyTrigger(KeyInput.KEY_LSHIFT)
  58. a| Keyboard: F1, F2, …
  59. a| KeyTrigger(KeyInput.KEY_F1) …
  60. a| Keyboard: Return, Enter
  61. <a| KeyTrigger(KeyInput.KEY_RETURN), +
  62. KeyTrigger(KeyInput.KEY_NUMPADENTER)
  63. a| Keyboard: PageUp, PageDown
  64. a| KeyTrigger(KeyInput.KEY_PGUP), +
  65. KeyTrigger(KeyInput.KEY_PGDN)
  66. a| Keyboard: Delete, Backspace
  67. a| KeyTrigger(KeyInput.KEY_BACK), +
  68. KeyTrigger(KeyInput.KEY_DELETE)
  69. a| Keyboard: Escape
  70. a| KeyTrigger(KeyInput.KEY_ESCAPE)
  71. a| Keyboard: Arrows
  72. a| KeyTrigger(KeyInput.KEY_DOWN), +
  73. KeyTrigger(KeyInput.KEY_UP) +
  74. KeyTrigger(KeyInput.KEY_LEFT), KeyTrigger(KeyInput.KEY_RIGHT)
  75. a| Joystick Button:
  76. a| JoyButtonTrigger(0, JoyInput.AXIS_POV_X), +
  77. JoyButtonTrigger(0, JoyInput.AXIS_POV_Y) ?
  78. a| Joystick Movement: Right
  79. a| JoyAxisTrigger(0, JoyInput.AXIS_POV_X, true)
  80. a| Joystick Movement: Left
  81. a| JoyAxisTrigger(0, JoyInput.AXIS_POV_X, false)
  82. a| Joystick Movement: Forward
  83. a| JoyAxisTrigger(0, JoyInput.AXIS_POV_Z, true)
  84. a| Joystick Movement: Backward
  85. a| JoyAxisTrigger(0, JoyInput.AXIS_POV_Z, false)
  86. |===
  87. In your IDE, use code completion to quickly look up Trigger literals. In the jMonkeyEngine SDK for example, press ctrl-space or ctrl-/ after `KeyInput.|` to choose from the list of all keys.
  88. == 2. Remove Default Trigger Mappings
  89. [source]
  90. ----
  91. inputManager.deleteMapping( SimpleApplication.INPUT_MAPPING_MEMORY );
  92. ----
  93. [cols="3", options="header"]
  94. |===
  95. a|Default Mapping
  96. a|Key
  97. a|Description
  98. a|INPUT_MAPPING_HIDE_STATS
  99. a|F5
  100. a|Hides the statistics in the bottom left.
  101. a|INPUT_MAPPING_CAMERA_POS
  102. a|KEY_C
  103. a|Prints debug output about the camera.
  104. a|INPUT_MAPPING_MEMORY
  105. a|KEY_M
  106. a|Prints debug output for memory usage.
  107. a|INPUT_MAPPING_EXIT
  108. a|KEY_ESCAPE
  109. a|Closes the application by calling `stop();`. Typically you do not remove this, unless you replace it by another way of quitting gracefully.
  110. |===
  111. == 3. Add Custom Trigger Mapping
  112. When initializing the application, add a Mapping for each Trigger.
  113. Give the mapping a meaningful name. The name should reflect the action, not the button/key (because buttons/keys can change). Here some examples:
  114. [source,java]
  115. ----
  116. inputManager.addMapping("Pause Game", new KeyTrigger(KeyInput.KEY_P));
  117. inputManager.addMapping("Rotate", new KeyTrigger(KeyInput.KEY_SPACE));
  118. ...
  119. ----
  120. 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:
  121. [source,java]
  122. ----
  123. inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A),
  124. new KeyTrigger(KeyInput.KEY_LEFT)); // A and left arrow
  125. inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D),
  126. new KeyTrigger(KeyInput.KEY_RIGHT)); // D and right arrow
  127. ...
  128. ----
  129. == 4. Create Listeners
  130. 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.
  131. [NOTE]
  132. ====
  133. 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.
  134. ====
  135. === ActionListener
  136. `com.jme3.input.controls.ActionListener`
  137. * Use for absolute “button pressed or released?, “on or off? actions.
  138. ** Examples: Pause/unpause, a rifle or revolver shot, jump, click to select.
  139. * JME gives you access to:
  140. ** The mapping name of the triggered action.
  141. ** A boolean whether the trigger is still pressed or has just been released.
  142. ** A float of the current time-per-frame as timing factor
  143. [source,java]
  144. ----
  145. private ActionListener actionListener = new ActionListener() {
  146. public void onAction(String name, boolean keyPressed, float tpf) {
  147. /** TODO: test for mapping names and implement actions */
  148. }
  149. };
  150. ----
  151. === AnalogListener
  152. `com.jme3.input.controls.AnalogListener`
  153. * Use for continuous and gradual actions.
  154. ** Examples: Walk, run, rotate, accelerate vehicle, strafe, (semi-)automatic weapon shot
  155. * JME gives you access to:
  156. ** The mapping name of the triggered action.
  157. ** A gradual float value between how long the trigger has been pressed.
  158. ** A float of the current time-per-frame as timing factor
  159. [source,java]
  160. ----
  161. private AnalogListener analogListener = new AnalogListener() {
  162. public void onAnalog(String name, float keyPressed, float tpf) {
  163. /** TODO: test for mapping names and implement actions */
  164. }
  165. };
  166. ----
  167. == 4. Register Mappings to Listeners
  168. 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.
  169. In the following example, you register the “Pause Game mapping to the `actionListener` object, because pausing a game is in “either/or decision.
  170. [source,java]
  171. ----
  172. inputManager.addListener(actionListener, new String[]{"Pause Game"});
  173. ----
  174. 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.
  175. [source,java]
  176. ----
  177. inputManager.addListener(analogListener, new String[]{"Left", "Right"});
  178. ----
  179. 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.
  180. [TIP]
  181. ====
  182. Did you register an action, but it does not work? Check the string's capitalization and spelling, it's case sensitive!
  183. ====
  184. == 5. Implement Actions in Listeners
  185. 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.
  186. Make use of the distinction between `if` and `else if` in this conditional.
  187. * If several actions can be triggered simultaneously, test for all of these with a series of bare `if`s. For example, a character can be running forward _and_ to the left.
  188. * If certain actions exclude one another, test for them with `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.
  189. === ActionListener
  190. 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 `&amp;&amp; !keyPressed`, like shown in the following example.
  191. [source,java]
  192. ----
  193. private ActionListener actionListener = new ActionListener() {
  194. public void onAction(String name, boolean keyPressed, float tpf) {
  195. if (name.equals("Pause Game") && !keyPressed) { // test?
  196. isRunning = !isRunning; // action!
  197. }
  198. if ...
  199. }
  200. };
  201. ----
  202. === AnalogListener
  203. The following example shows how you define actions with an AnalogListener. These 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.
  204. [source,java]
  205. ----
  206. private AnalogListener analogListener = new AnalogListener() {
  207. public void onAnalog(String name, float value, float tpf) {
  208. if (name.equals("Rotate")) { // test?
  209. player.rotate(0, value*speed, 0); // action!
  210. }
  211. if ...
  212. }
  213. };
  214. ----
  215. == Let Users Remap Keys
  216. 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.
  217. 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.
  218. == Detecting Joystick Connection States
  219. For information regarding the connection state of a joystick see <<jme3/beginner/hello_input_system.adoc#listening-for-joystick-connections, Listening for Joystick Connections>>.