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