| quddusc | 1dff746 | 2013-07-01 16:57:26 -0700 | [diff] [blame^] | 1 | page.title=Handling Controller Actions |
| 2 | trainingnavtop=true |
| 3 | |
| 4 | @jd:body |
| 5 | |
| 6 | <!-- This is the training bar --> |
| 7 | <div id="tb-wrapper"> |
| 8 | <div id="tb"> |
| 9 | |
| 10 | <h2>This lesson teaches you to</h2> |
| 11 | <ol> |
| 12 | <li><a href="#input">Verify a Game Controller is Connected</a></li> |
| 13 | <li><a href="#button">Process Gamepad Button Presses</a> |
| 14 | </li> |
| 15 | <li><a href="#dpad">Process Directional Pad Input</a> |
| 16 | </li> |
| 17 | <li><a href="#joystick">Process Joystick Movements</a> |
| 18 | </li> |
| 19 | </ol> |
| 20 | |
| 21 | <h2>Try it out</h2> |
| 22 | <div class="download-box"> |
| 23 | <a href="http://developer.android.com/shareables/training/ControllerSample.zip" |
| 24 | class="button">Download the sample</a> |
| 25 | <p class="filename">ControllerSample.zip</p> |
| 26 | </div> |
| 27 | |
| 28 | </div> |
| 29 | </div> |
| 30 | |
| 31 | <p>At the system level, Android reports input event codes from game controllers |
| 32 | as Android key codes and axis values. In your game, you can receive these codes |
| 33 | and values and convert them to specific in-game actions.</p> |
| 34 | |
| 35 | <p>When players physically connect or wirelessly pair a game controller to |
| 36 | their Android-powered devices, the system auto-detects the controller |
| 37 | as an input device and starts reporting its input events. Your game can receive |
| 38 | these input events by implementing the following callback methods in your active |
| 39 | {@link android.app.Activity} or focused {@link android.view.View} (you should |
| 40 | implement the callbacks for either the {@link android.app.Activity} or |
| 41 | {@link android.view.View}, but not both): </p> |
| 42 | |
| 43 | <ul> |
| 44 | <li>From {@link android.app.Activity}: |
| 45 | <ul> |
| 46 | <li>{@link android.app.Activity#dispatchGenericMotionEvent(android.view.MotionEvent) dispatchGenericMotionEvent(android.view.MotionEvent)} |
| 47 | <p>Called to process generic motion events such as joystick movements.</p> |
| 48 | </li> |
| 49 | <li>{@link android.app.Activity#dispatchKeyEvent(android.view.KeyEvent) dispatchKeyEvent(android.view.KeyEvent)} |
| 50 | <p>Called to process key events such as a press or release of a |
| 51 | gamepad or D-pad button.</p> |
| 52 | </li> |
| 53 | </ul> |
| 54 | </li> |
| 55 | <li>From {@link android.view.View}: |
| 56 | <ul> |
| 57 | <li>{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent) |
| 58 | onGenericMotionEvent(android.view.MotionEvent)} |
| 59 | <p>Called to process generic motion events such as joystick movements.</p> |
| 60 | </li> |
| 61 | <li>{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown(int, android.view.KeyEvent)} |
| 62 | <p>Called to process a press of a physical key such as a gamepad or |
| 63 | D-pad button.</p> |
| 64 | </li> |
| 65 | <li>{@link android.view.View#onKeyUp(int, android.view.KeyEvent) onKeyUp(int, android.view.KeyEvent)} |
| 66 | <p>Called to process a release of a physical key such as a gamepad or |
| 67 | D-pad button.</p> |
| 68 | </li> |
| 69 | </ul> |
| 70 | </li> |
| 71 | </ul> |
| 72 | |
| 73 | <p>The recommended approach is to capture the events from the |
| 74 | specific {@link android.view.View} object that the user interacts with. |
| 75 | Inspect the following objects provided by the callbacks to get information |
| 76 | about the type of input event received:</p> |
| 77 | |
| 78 | <dl> |
| 79 | <dt>{@link android.view.KeyEvent}</dt> |
| 80 | <dd>An object that describes directional |
| 81 | pad</a> (D-pad) and gamepad button events. Key events are accompanied by a |
| 82 | <em>key code</em> that indicates the specific button triggered, such as |
| 83 | {@link android.view.KeyEvent#KEYCODE_DPAD_DOWN DPAD_DOWN} |
| 84 | or {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}. You can obtain the |
| 85 | key code by calling {@link android.view.KeyEvent#getKeyCode()} or from key |
| 86 | event callbacks such as |
| 87 | {@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()}. |
| 88 | <dd> |
| 89 | <dt>{@link android.view.MotionEvent}</dt> |
| 90 | <dd>An object that describes input from joystick and shoulder trigger |
| 91 | movements. Motion events are accompanied by an action code and a set of |
| 92 | <em>axis values</em>. The action code specifies the state change that occurred |
| 93 | such as a joystick being moved. The axis values describe the position and other |
| 94 | movement properties for a specific physical control, such as |
| 95 | {@link android.view.MotionEvent#AXIS_X} or |
| 96 | {@link android.view.MotionEvent#AXIS_RTRIGGER}. You can obtain the action code |
| 97 | by calling {@link android.view.MotionEvent#getAction()} and the axis value by |
| 98 | calling {@link android.view.MotionEvent#getAxisValue(int) getAxisValue()}. |
| 99 | <dd> |
| 100 | </dl> |
| 101 | <p>This lesson focuses on how you can handle input from the most common types of |
| 102 | physical controls (gamepad buttons, directional pads, and |
| 103 | joysticks) in a game screen by implementing the above-mentioned |
| 104 | {@link android.view.View} callback methods and processing |
| 105 | {@link android.view.KeyEvent} and {@link android.view.MotionEvent} objects.</p> |
| 106 | |
| 107 | <h2 id="input">Verify a Game Controller is Connected</h2> |
| 108 | <p>When reporting input events, Android does not distinguish |
| 109 | between events that came from a non-game controller device and events that came |
| 110 | from a game controller. For example, a touch screen action generates an |
| 111 | {@link android.view.MotionEvent#AXIS_X} event that represents the X |
| 112 | coordinate of the touch surface, but a joystick generates an {@link android.view.MotionEvent#AXIS_X} event that represents the X position of the joystick. If |
| 113 | your game cares about handling game-controller input, you should first check |
| 114 | that the input event comes from a relevant source type.</p> |
| 115 | <p>To verify that a connected input device is a game controller, call |
| 116 | {@link android.view.InputDevice#getSources()} to obtain a combined bit field of |
| 117 | input source types supported on that device. You can then test to see if |
| 118 | the following fields are set:</p> |
| 119 | <ul> |
| 120 | <li>A source type of {@link android.view.InputDevice#SOURCE_GAMEPAD} indicates |
| 121 | that the input device has gamepad buttons (for example, |
| 122 | {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}). Note that this source |
| 123 | type does not strictly indicate if the game controller has D-pad buttons, |
| 124 | although most gamepads typically have directional controls.</li> |
| 125 | <li>A source type of {@link android.view.InputDevice#SOURCE_DPAD} indicates that |
| 126 | the input device has D-pad buttons (for example, |
| 127 | {@link android.view.KeyEvent#KEYCODE_DPAD_UP DPAD_UP}).</li> |
| 128 | <li>A source type of {@link android.view.InputDevice#SOURCE_JOYSTICK} |
| 129 | indicates that the input device has analog control sticks (for example, a |
| 130 | joystick that records movements along {@link android.view.MotionEvent#AXIS_X} |
| 131 | and {@link android.view.MotionEvent#AXIS_Y}).</li> |
| 132 | </ul> |
| 133 | <p>The following code snippet shows a helper method that lets you check whether |
| 134 | the connected input devices are game controllers. If so, the method retrieves |
| 135 | the device IDs for the game controllers. You can then associate each device |
| 136 | ID with a player in your game, and process game actions for each connected |
| 137 | player separately. To learn more about supporting multiple game controllers |
| 138 | that are simultaneously connected on the same Android device, see |
| 139 | <a href="multiple-controllers.html">Supporting Multiple Game Controllers</a>.</p> |
| 140 | <pre> |
| 141 | public ArrayList<Integer> getGameControllerIds() { |
| 142 | ArrayList<Integer> gameControllerDeviceIds = new ArrayList<Integer>(); |
| 143 | int[] deviceIds = InputDevice.getDeviceIds(); |
| 144 | for (int deviceId : deviceIds) { |
| 145 | InputDevice dev = InputDevice.getDevice(deviceId); |
| 146 | int sources = dev.getSources(); |
| 147 | |
| 148 | // Verify that the device has gamepad buttons, control sticks, or both. |
| 149 | if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) |
| 150 | || ((sources & InputDevice.SOURCE_JOYSTICK) |
| 151 | == InputDevice.SOURCE_JOYSTICK)) { |
| 152 | // This device is a game controller. Store its device ID. |
| 153 | if (!gameControllerDeviceIds.contains(deviceId)) { |
| 154 | gameControllerDeviceIds.add(deviceId); |
| 155 | } |
| 156 | } |
| 157 | } |
| 158 | return gameControllerDeviceIds; |
| 159 | } |
| 160 | </pre> |
| 161 | <p>Additionally, you might want to check for individual input capabilities |
| 162 | supported by a connected game controller. This might be useful, for example, if |
| 163 | you want your game to use only input from the set of physical controls it |
| 164 | understands.</p> |
| 165 | <p>To detect if a specific key code or axis code is supported by a connected |
| 166 | game controller, use these techniques:</p> |
| 167 | <ul> |
| 168 | <li>In Android 4.4 (API level 19) or higher, you can determine if a key code is |
| 169 | supported on a connected game controller by calling |
| 170 | {@link android.view.InputDevice#hasKeys(int...)}.</li> |
| 171 | <li>In Android 3.1 (API level 12) or higher, you can find all available axes |
| 172 | supported on a connected game controller by first calling |
| 173 | {@link android.view.InputDevice#getMotionRanges()}. Then, on each |
| 174 | {@link android.view.InputDevice.MotionRange} object returned, call |
| 175 | {@link android.view.InputDevice.MotionRange#getAxis()} to get its axis ID.</li> |
| 176 | </ul> |
| 177 | |
| 178 | <h2 id="button">Process Gamepad Button Presses</h2> |
| 179 | <p>Figure 1 shows how Android maps key codes and axis values to the physical |
| 180 | controls on most game controllers.</p> |
| 181 | <img src="{@docRoot}images/training/game-controller-profiles.png" alt="" |
| 182 | id="figure1" /> |
| 183 | <p class="img-caption"> |
| 184 | <strong>Figure 1.</strong> Profile for a generic game controller. |
| 185 | </p> |
| 186 | <p>The callouts in the figure refer to the following:</p> |
| 187 | <div style="-moz-column-count:2;-webkit-column-count:2;column-count:2;"> |
| 188 | <ol style="margin-left:30px;list-style:decimal;"> |
| 189 | <li>{@link android.view.MotionEvent#AXIS_HAT_X}, |
| 190 | {@link android.view.MotionEvent#AXIS_HAT_Y}, |
| 191 | {@link android.view.KeyEvent#KEYCODE_DPAD_UP DPAD_UP}, |
| 192 | {@link android.view.KeyEvent#KEYCODE_DPAD_DOWN DPAD_DOWN}, |
| 193 | {@link android.view.KeyEvent#KEYCODE_DPAD_LEFT DPAD_LEFT}, |
| 194 | {@link android.view.KeyEvent#KEYCODE_DPAD_RIGHT DPAD_RIGHT} |
| 195 | </li> |
| 196 | <li>{@link android.view.MotionEvent#AXIS_X}, |
| 197 | {@link android.view.MotionEvent#AXIS_Y}, |
| 198 | {@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBL BUTTON_THUMBL}</li> |
| 199 | <li>{@link android.view.MotionEvent#AXIS_Z}, |
| 200 | {@link android.view.MotionEvent#AXIS_RZ}, |
| 201 | {@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBR BUTTON_THUMBR}</li> |
| 202 | <li>{@link android.view.KeyEvent#KEYCODE_BUTTON_X BUTTON_X}</li> |
| 203 | <li>{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}</li> |
| 204 | <li>{@link android.view.KeyEvent#KEYCODE_BUTTON_Y BUTTON_Y}</li> |
| 205 | <li>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}</li> |
| 206 | <li>{@link android.view.KeyEvent#KEYCODE_BUTTON_R1 BUTTON_R1}</li> |
| 207 | <li>{@link android.view.MotionEvent#AXIS_RTRIGGER}, |
| 208 | {@link android.view.MotionEvent#AXIS_THROTTLE}</li> |
| 209 | <li>{@link android.view.MotionEvent#AXIS_LTRIGGER}, |
| 210 | {@link android.view.MotionEvent#AXIS_BRAKE}</li> |
| 211 | <li>{@link android.view.KeyEvent#KEYCODE_BUTTON_L1 BUTTON_L1}</li> |
| 212 | </ol> |
| 213 | </div> |
| 214 | <p>Common key codes generated by gamepad button presses include |
| 215 | {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}, |
| 216 | {@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}, |
| 217 | {@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT}, |
| 218 | and {@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START}. Some game |
| 219 | controllers also trigger the {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER |
| 220 | DPAD_CENTER} key code when the center of the D-pad crossbar is pressed. Your |
| 221 | game can inspect the key code by calling {@link android.view.KeyEvent#getKeyCode()} |
| 222 | or from key event callbacks such as |
| 223 | {@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()}, |
| 224 | and if it represents an event that is relevant to your game, process it as a |
| 225 | game action. Table 1 lists the recommended game actions for the most common |
| 226 | gamepad buttons. |
| 227 | </p> |
| 228 | |
| 229 | <p class="table-caption" id="table1"> |
| 230 | <strong>Table 1.</strong> Recommended game actions for gamepad |
| 231 | buttons.</p> |
| 232 | <table> |
| 233 | <tr> |
| 234 | <th scope="col">Game Action</th> |
| 235 | <th scope="col">Button Key Code</th> |
| 236 | </tr> |
| 237 | <tr> |
| 238 | <td>Start game in main menu, or pause/unpause during game</td> |
| 239 | <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START}</td> |
| 240 | </tr> |
| 241 | <tr> |
| 242 | <td>Display menu</td> |
| 243 | <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT} and |
| 244 | {@link android.view.KeyEvent#KEYCODE_MENU}</td> |
| 245 | </tr> |
| 246 | <tr> |
| 247 | <td>Same as Android <em>Back</em></td> |
| 248 | <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}<sup>*</sup> and |
| 249 | {@link android.view.KeyEvent#KEYCODE_BACK KEYCODE_BACK}</td> |
| 250 | </tr> |
| 251 | <tr> |
| 252 | <td>Confirm selection, or perform primary game action</td> |
| 253 | <td>{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}<sup>*</sup> and |
| 254 | {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER}</td> |
| 255 | </tr> |
| 256 | </table> |
| 257 | <p> |
| 258 | <em>* This could be the opposite button (A/B), depending on the locale that |
| 259 | you are supporting.</em> |
| 260 | </p> |
| 261 | |
| 262 | <p class="note"><strong>Tip: </strong>Consider providing a configuration screen |
| 263 | in your game to allow users to personalize their own game controller mappings for |
| 264 | game actions.</p> |
| 265 | |
| 266 | <p>The following snippet shows how you might override |
| 267 | {@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()} to |
| 268 | associate the {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} and |
| 269 | {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER} button presses |
| 270 | with a game action. |
| 271 | </p> |
| 272 | <pre> |
| 273 | public class GameView extends View { |
| 274 | ... |
| 275 | |
| 276 | @Override |
| 277 | public boolean onKeyDown(int keyCode, KeyEvent event) { |
| 278 | boolean handled = false; |
| 279 | if ((event.getSource() & InputDevice.SOURCE_GAMEPAD) |
| 280 | == InputDevice.SOURCE_GAMEPAD) { |
| 281 | if (event.getRepeatCount() == 0) { |
| 282 | switch (keyCode) { |
| 283 | // Handle gamepad and D-pad button presses to |
| 284 | // navigate the ship |
| 285 | ... |
| 286 | |
| 287 | default: |
| 288 | if (isFireKey(keyCode)) { |
| 289 | // Update the ship object to fire lasers |
| 290 | ... |
| 291 | handled = true; |
| 292 | } |
| 293 | break; |
| 294 | } |
| 295 | } |
| 296 | if (handled) { |
| 297 | return true; |
| 298 | } |
| 299 | } |
| 300 | return super.onKeyDown(keyCode, event); |
| 301 | } |
| 302 | |
| 303 | private static boolean isFireKey(int keyCode) { |
| 304 | // Here we treat Button_A and DPAD_CENTER as the primary action |
| 305 | // keys for the game. You may need to switch this to Button_B and |
| 306 | // DPAD_CENTER depending on the user expectations for the locale |
| 307 | // in which your game runs. |
| 308 | return keyCode == KeyEvent.KEYCODE_DPAD_CENTER |
| 309 | || keyCode == KeyEvent.KEYCODE_BUTTON_A; |
| 310 | } |
| 311 | } |
| 312 | </pre> |
| 313 | |
| 314 | <p>Follow these best practices when handling button presses:</p> |
| 315 | <ul> |
| 316 | <li><strong>Provide localized button mappings.</strong> Generally, if your game |
| 317 | has a primary gameplay action (for example, it fires lasers, lets your avatar |
| 318 | do a high jump, or confirms an item selection), you should map |
| 319 | both {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER} and |
| 320 | {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} to this action. However, |
| 321 | in some locales, users may expect |
| 322 | {@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B} to be the confirm |
| 323 | button and {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} to be the |
| 324 | back button instead. If you are supporting these locales, make sure to treat |
| 325 | the A and B buttons accordingly in your game. To determine the user's locale, |
| 326 | call the {@link java.util.Locale#getDefault()} method. |
| 327 | <li><strong>Map {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} |
| 328 | consistently across different Android versions.</strong> On Android 4.2 (API |
| 329 | level 17) and lower, the system treats |
| 330 | {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} as the Android |
| 331 | <em>Back</em> key by default. If your app supports these Android |
| 332 | versions, make sure to treat |
| 333 | {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} as the primary game |
| 334 | action (except in the localization case mentioned |
| 335 | above). To determine the current Android SDK |
| 336 | version on the device, refer to the |
| 337 | {@link android.os.Build.VERSION#SDK_INT Build.VERSION.SDK_INT} value. |
| 338 | </li> |
| 339 | </ul> |
| 340 | |
| 341 | <h2 id="dpad">Process Directional Pad Input</h2> |
| 342 | <p>The 4-way directional pad (D-pad) is a common physical control in many game |
| 343 | controllers. Android reports D-pad UP and DOWN presses as |
| 344 | {@link android.view.MotionEvent#AXIS_HAT_Y} events with a range |
| 345 | from -1.0 (up) to 1.0 (down), and D-pad LEFT or RIGHT presses as |
| 346 | {@link android.view.MotionEvent#AXIS_HAT_X} events with a range from -1.0 |
| 347 | (left) to 1.0 (right).</p> |
| 348 | <p>Some controllers instead report D-pad presses with a key code. If your game |
| 349 | cares about D-pad presses, you should treat the hat axis events and the D-pad |
| 350 | key codes as the same input events, as recommended in table 2.</p> |
| 351 | <p class="table-caption" id="table2"> |
| 352 | <strong>Table 2.</strong> Recommended default game actions for D-pad key |
| 353 | codes and hat axis values.</p> |
| 354 | <table> |
| 355 | <tr> |
| 356 | <th scope="col">Game Action</th> |
| 357 | <th scope="col">D-pad Key Code</th> |
| 358 | <th scope="col">Hat Axis Code</th> |
| 359 | </tr> |
| 360 | <tr> |
| 361 | <td>Move Up</td> |
| 362 | <td>{@link android.view.KeyEvent#KEYCODE_DPAD_UP}</td> |
| 363 | <td>{@link android.view.MotionEvent#AXIS_HAT_Y} (for values 0 to -1.0)</td> |
| 364 | </tr> |
| 365 | <tr> |
| 366 | <td>Move Down</td> |
| 367 | <td>{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN}</td> |
| 368 | <td>{@link android.view.MotionEvent#AXIS_HAT_Y} (for values 0 to 1.0)</td> |
| 369 | </tr> |
| 370 | <tr> |
| 371 | <td>Move Left</td> |
| 372 | <td>{@link android.view.KeyEvent#KEYCODE_DPAD_LEFT}</td> |
| 373 | <td>{@link android.view.MotionEvent#AXIS_HAT_X} (for values 0 to -1.0)</td> |
| 374 | </tr> |
| 375 | <tr> |
| 376 | <td>Move Right</td> |
| 377 | <td>{@link android.view.KeyEvent#KEYCODE_DPAD_RIGHT}</td> |
| 378 | <td>{@link android.view.MotionEvent#AXIS_HAT_X} (for values 0 to 1.0)</td> |
| 379 | </tr> |
| 380 | </table> |
| 381 | |
| 382 | |
| 383 | <p>The following code snippet shows a helper class that lets you check the hat |
| 384 | axis and key code values from an input event to determine the D-pad direction. |
| 385 | </p> |
| 386 | <pre> |
| 387 | public class Dpad { |
| 388 | final static int UP = 0; |
| 389 | final static int LEFT = 1; |
| 390 | final static int RIGHT = 2; |
| 391 | final static int DOWN = 3; |
| 392 | final static int CENTER = 4; |
| 393 | |
| 394 | int directionPressed = -1; // initialized to -1 |
| 395 | |
| 396 | public int getDirectionPressed(InputEvent event) { |
| 397 | if (!isDpadDevice(event)) { |
| 398 | return -1; |
| 399 | } |
| 400 | |
| 401 | // If the input event is a MotionEvent, check its hat axis values. |
| 402 | if (event instanceof MotionEvent) { |
| 403 | |
| 404 | // Use the hat axis value to find the D-pad direction |
| 405 | MotionEvent motionEvent = (MotionEvent) event; |
| 406 | float xaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X); |
| 407 | float yaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y); |
| 408 | |
| 409 | // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad |
| 410 | // LEFT and RIGHT direction accordingly. |
| 411 | if (Float.compare(xaxis, -1.0f) == 0) { |
| 412 | directionPressed = Dpad.LEFT; |
| 413 | } else if (Float.compare(xaxis, 1.0f) == 0) { |
| 414 | directionPressed = Dpad.RIGHT; |
| 415 | } |
| 416 | // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad |
| 417 | // UP and DOWN direction accordingly. |
| 418 | else if (Float.compare(yaxis, -1.0f) == 0) { |
| 419 | directionPressed = Dpad.UP; |
| 420 | } else if (Float.compare(yaxis, -1.0f) == 0) { |
| 421 | directionPressed = Dpad.DOWN; |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | // If the input event is a KeyEvent, check its key code. |
| 426 | else if (event instanceof KeyEvent) { |
| 427 | |
| 428 | // Use the key code to find the D-pad direction. |
| 429 | KeyEvent keyEvent = (KeyEvent) event; |
| 430 | if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) { |
| 431 | directionPressed = Dpad.LEFT; |
| 432 | } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) { |
| 433 | directionPressed = Dpad.RIGHT; |
| 434 | } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) { |
| 435 | directionPressed = Dpad.UP; |
| 436 | } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) { |
| 437 | directionPressed = Dpad.DOWN; |
| 438 | } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) { |
| 439 | directionPressed = Dpad.CENTER; |
| 440 | } |
| 441 | } |
| 442 | return directionPressed; |
| 443 | } |
| 444 | |
| 445 | public static boolean isDpadDevice(InputEvent event) { |
| 446 | // Check that input comes from a device with directional pads. |
| 447 | if ((event.getSource() & InputDevice.SOURCE_DPAD) |
| 448 | != InputDevice.SOURCE_DPAD) { |
| 449 | return true; |
| 450 | } else { |
| 451 | return false; |
| 452 | } |
| 453 | } |
| 454 | } |
| 455 | </pre> |
| 456 | |
| 457 | <p>You can use this helper class in your game wherever you want to process |
| 458 | D-pad input (for example, in the |
| 459 | {@link android.view.View#onGenericMotionEvent(android.view.MotionEvent) |
| 460 | onGenericMotionEvent()} or |
| 461 | {@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()} |
| 462 | callbacks).</p> |
| 463 | <p>For example:</p> |
| 464 | <pre> |
| 465 | Dpad mDpad = new Dpad(); |
| 466 | ... |
| 467 | @Override |
| 468 | public boolean onGenericMotionEvent(MotionEvent event) { |
| 469 | |
| 470 | // Check if this event if from a D-pad and process accordingly. |
| 471 | if (Dpad.isDpadDevice(event)) { |
| 472 | |
| 473 | int press = mDpad.getDirectionPressed(event); |
| 474 | switch (press) { |
| 475 | case LEFT: |
| 476 | // Do something for LEFT direction press |
| 477 | ... |
| 478 | return true; |
| 479 | case RIGHT: |
| 480 | // Do something for RIGHT direction press |
| 481 | ... |
| 482 | return true; |
| 483 | case UP: |
| 484 | // Do something for UP direction press |
| 485 | ... |
| 486 | return true; |
| 487 | ... |
| 488 | } |
| 489 | } |
| 490 | |
| 491 | // Check if this event is from a joystick movement and process accordingly. |
| 492 | ... |
| 493 | } |
| 494 | </pre> |
| 495 | |
| 496 | <h2 id="joystick">Process Joystick Movements</h2> |
| 497 | <p>When players move a joystick on their game controllers, Android reports a |
| 498 | {@link android.view.MotionEvent} that contains the |
| 499 | {@link android.view.MotionEvent#ACTION_MOVE} action code and the updated |
| 500 | positions of the joystick's axes. Your game can use the data provided by |
| 501 | the {@link android.view.MotionEvent} to determine if a joystick movement it |
| 502 | cares about happened. |
| 503 | </p> |
| 504 | <p>Note that joystick motion events may batch multiple movement samples together |
| 505 | within a single object. The {@link android.view.MotionEvent} object contains |
| 506 | the current position for each joystick axis as well as multiple historical |
| 507 | positions for each axis. When reporting motion events with action code {@link android.view.MotionEvent#ACTION_MOVE} (such as joystick movements), Android batches up the |
| 508 | axis values for efficiency. The historical values for an axis consists of the |
| 509 | set of distinct values older than the current axis value, and more recent than |
| 510 | values reported in any previous motion events. See the |
| 511 | {@link android.view.MotionEvent} reference for details.</p> |
| 512 | <p>You can use the historical information to more accurately render a game |
| 513 | object's movement based on the joystick input. To |
| 514 | retrieve the current and historical values, call |
| 515 | {@link android.view.MotionEvent#getAxisValue(int) |
| 516 | getAxisValue()} or {@link android.view.MotionEvent#getHistoricalAxisValue(int, |
| 517 | int) getHistoricalAxisValue()}. You can also find the number of historical |
| 518 | points in the joystick event by calling |
| 519 | {@link android.view.MotionEvent#getHistorySize()}.</p> |
| 520 | <p>The following snippet shows how you might override the |
| 521 | {@link android.view.View#onGenericMotionEvent(android.view.MotionEvent) |
| 522 | onGenericMotionEvent()} callback to process joystick input. You should first |
| 523 | process the historical values for an axis, then process its current position. |
| 524 | </p> |
| 525 | <pre> |
| 526 | public class GameView extends View { |
| 527 | |
| 528 | @Override |
| 529 | public boolean onGenericMotionEvent(MotionEvent event) { |
| 530 | |
| 531 | // Check that the event came from a game controller |
| 532 | if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == |
| 533 | InputDevice.SOURCE_JOYSTICK && |
| 534 | event.getAction() == MotionEvent.ACTION_MOVE) |
| 535 | |
| 536 | // Process all historical movement samples in the batch |
| 537 | final int historySize = event.getHistorySize(); |
| 538 | |
| 539 | // Process the movements starting from the |
| 540 | // earliest historical position in the batch |
| 541 | for (int i = 0; i < historySize; i++) { |
| 542 | // Process the event at historical position i |
| 543 | processJoystickInput(event, i); |
| 544 | } |
| 545 | |
| 546 | // Process the current movement sample in the batch (position -1) |
| 547 | processJoystickInput(event, -1); |
| 548 | return true; |
| 549 | } |
| 550 | return super.onGenericMotionEvent(event); |
| 551 | } |
| 552 | } |
| 553 | </pre> |
| 554 | <p>Before using joystick input, you need to determine if the joystick is |
| 555 | centered, then calculate its axis movements accordingly. Joysticks typically |
| 556 | have a <em>flat</em> area, that is, a range of values near the (0,0) coordinate |
| 557 | at which the axis is considered to be centered. If the axis value reported by |
| 558 | Android falls within the flat area, you should treat the controller to be at |
| 559 | rest (that is, motionless along both axes).</p> |
| 560 | <p>The snippet below shows a helper method that calculates the movement along |
| 561 | each axis. You invoke this helper in the {@code processJoystickInput()} method |
| 562 | described further below. |
| 563 | </p> |
| 564 | <pre> |
| 565 | private static float getCenteredAxis(MotionEvent event, |
| 566 | InputDevice device, int axis, int historyPos) { |
| 567 | final InputDevice.MotionRange range = |
| 568 | device.getMotionRange(axis, event.getSource()); |
| 569 | |
| 570 | // A joystick at rest does not always report an absolute position of |
| 571 | // (0,0). Use the getFlat() method to determine the range of values |
| 572 | // bounding the joystick axis center. |
| 573 | if (range != null) { |
| 574 | final float flat = range.getFlat(); |
| 575 | final float value = |
| 576 | historyPos < 0 ? event.getAxisValue(axis): |
| 577 | event.getHistoricalAxisValue(axis, historyPos); |
| 578 | |
| 579 | // Ignore axis values that are within the 'flat' region of the |
| 580 | // joystick axis center. |
| 581 | if (Math.abs(value) > flat) { |
| 582 | return value; |
| 583 | } |
| 584 | } |
| 585 | return 0; |
| 586 | } |
| 587 | </pre> |
| 588 | <p>Putting it all together, here is how you might process joystick movements in |
| 589 | your game:</p> |
| 590 | <pre> |
| 591 | private void processJoystickInput(MotionEvent event, |
| 592 | int historyPos) { |
| 593 | |
| 594 | InputDevice mInputDevice = event.getDevice(); |
| 595 | |
| 596 | // Calculate the horizontal distance to move by |
| 597 | // using the input value from one of these physical controls: |
| 598 | // the left control stick, hat axis, or the right control stick. |
| 599 | float x = getCenteredAxis(event, mInputDevice, |
| 600 | MotionEvent.AXIS_X, historyPos); |
| 601 | if (x == 0) { |
| 602 | x = getCenteredAxis(event, mInputDevice, |
| 603 | MotionEvent.AXIS_HAT_X, historyPos); |
| 604 | } |
| 605 | if (x == 0) { |
| 606 | x = getCenteredAxis(event, mInputDevice, |
| 607 | MotionEvent.AXIS_Z, historyPos); |
| 608 | } |
| 609 | |
| 610 | // Calculate the vertical distance to move by |
| 611 | // using the input value from one of these physical controls: |
| 612 | // the left control stick, hat switch, or the right control stick. |
| 613 | float y = getCenteredAxis(event, mInputDevice, |
| 614 | MotionEvent.AXIS_Y, historyPos); |
| 615 | if (y == 0) { |
| 616 | y = getCenteredAxis(event, mInputDevice, |
| 617 | MotionEvent.AXIS_HAT_Y, historyPos); |
| 618 | } |
| 619 | if (y == 0) { |
| 620 | y = getCenteredAxis(event, mInputDevice, |
| 621 | MotionEvent.AXIS_RZ, historyPos); |
| 622 | } |
| 623 | |
| 624 | // Update the ship object based on the new x and y values |
| 625 | ... |
| 626 | |
| 627 | return true; |
| 628 | } |
| 629 | </pre> |
| 630 | <p>To support game controllers that have more sophisticated |
| 631 | features beyond a single joystick, follow these best practices: </p> |
| 632 | <ul> |
| 633 | <li><strong>Handle dual controller sticks.</strong> Many game controllers have |
| 634 | both a left and right joystick. For the left stick, Android |
| 635 | reports horizontal movements as {@link android.view.MotionEvent#AXIS_X} events |
| 636 | and vertical movements as {@link android.view.MotionEvent#AXIS_Y} events. |
| 637 | For the right stick, Android reports horizontal movements as |
| 638 | {@link android.view.MotionEvent#AXIS_Z} events and vertical movements as |
| 639 | {@link android.view.MotionEvent#AXIS_RZ} events. Make sure to handle |
| 640 | both controller sticks in your code.</li> |
| 641 | <li><strong>Handle shoulder trigger presses (but provide alternative input |
| 642 | methods).</strong> Some controllers have left and right shoulder |
| 643 | triggers. If these triggers are present, Android reports a left trigger press |
| 644 | as an {@link android.view.MotionEvent#AXIS_LTRIGGER} event and a |
| 645 | right trigger press as an |
| 646 | {@link android.view.MotionEvent#AXIS_RTRIGGER} event. On Android |
| 647 | 4.3 (API level 18), a controller that produces a |
| 648 | {@link android.view.MotionEvent#AXIS_LTRIGGER} also reports an |
| 649 | identical value for the {@link android.view.MotionEvent#AXIS_BRAKE} axis. The |
| 650 | same is true for {@link android.view.MotionEvent#AXIS_RTRIGGER} and |
| 651 | {@link android.view.MotionEvent#AXIS_GAS}. Android reports all analog trigger |
| 652 | presses with a normalized value from 0.0 (released) to 1.0 (fully pressed). Not |
| 653 | all controllers have triggers, so consider allowing players to perform those |
| 654 | game actions with other buttons. |
| 655 | </li> |
| 656 | </ul> |