Home | History | Annotate | Download | only in game-controllers
      1 page.title=Supporting Multiple Game Controllers
      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="#map">Map Players to Controller Device IDs</a></li>
     13   <li><a href="#detect">Process Multiple Controller Input</a></li>
     14 </ol>
     15 
     16 <h2>Try it out</h2>
     17 <div class="download-box">
     18   <a href="http://developer.android.com/shareables/training/ControllerSample.zip"
     19 class="button">Download the sample</a>
     20   <p class="filename">ControllerSample.zip</p>
     21 </div>
     22 
     23 
     24 </div>
     25 </div>
     26 <p>While most games are designed to support a single user per Android device,
     27 it's also possible to support multiple users with game controllers that are
     28 connected simultaneously on the same Android device.</p>
     29 <p>This lesson covers some basic techniques for handling input in your single
     30 device multiplayer game from multiple connected controllers. This includes
     31 maintaining a mapping between player avatars and each controller device and
     32 processing controller input events appropriately.
     33 </p>
     34 
     35 <h2 id="map">Map Players to Controller Device IDs</h2>
     36 <p>When a game controller is connected to an Android device, the system
     37 assigns it an integer device ID. You can obtain the device IDs for connected
     38 game controllers by calling {@link android.view.InputDevice#getDeviceIds() InputDevice.getDeviceIds()}, as shown in <a href="controller-input.html#input">Verify a Game Controller is Connected</a>. You can then associate each
     39 device ID with a player in your game, and process game actions for each player separately.
     40 </p>
     41 <p class="note"><strong>Note: </strong>On devices running Android 4.1 (API
     42 level 16) and higher, you can obtain an input devices descriptor using
     43 {@link android.view.InputDevice#getDescriptor()}, which returns a unique
     44 persistent string value for the input device. Unlike a device ID, the descriptor
     45 value won't change even if the input device is disconnected, reconnected, or
     46 reconfigured.
     47 </p>
     48 <p>The code snippet below shows how to use a {@link android.util.SparseArray}
     49 to associate a player's avatar with a specific controller. In this example, the
     50 {@code mShips} variable stores a collection of {@code Ship} objects. A new
     51 player avatar is created in-game when a new controller is attached by a user,
     52 and removed when its associated controller is removed.
     53 </p>
     54 <p>The {@code onInputDeviceAdded()} and {@code onInputDeviceRemoved()} callback
     55 methods are part of the abstraction layer introduced in
     56 <a href="{@docRoot}training/game-controllers/compatibility.html#status_callbacks}">
     57 Supporting Controllers Across Android Versions</a>. By implementing these
     58 listener callbacks, your game can identify the game controller's device ID when a
     59 controller is added or removed. This detection is compatible with Android 2.3
     60 (API level 9) and higher.
     61 </p>
     62 
     63 <pre>
     64 private final SparseArray&lt;Ship&gt; mShips = new SparseArray&lt;Ship&gt;();
     65 
     66 &#64;Override
     67 public void onInputDeviceAdded(int deviceId) {
     68     getShipForID(deviceId);
     69 }
     70 
     71 &#64;Override
     72 public void onInputDeviceRemoved(int deviceId) {
     73     removeShipForID(deviceId);
     74 }
     75 
     76 private Ship getShipForID(int shipID) {
     77     Ship currentShip = mShips.get(shipID);
     78     if ( null == currentShip ) {
     79         currentShip = new Ship();
     80         mShips.append(shipID, currentShip);
     81     }
     82     return currentShip;
     83 }
     84 
     85 private void removeShipForID(int shipID) {
     86     mShips.remove(shipID);
     87 }
     88 </pre>
     89 
     90 <h2 id="detect">Process Multiple Controller Input</h2>
     91 <p>Your game should execute the following loop to process
     92 input from multiple controllers:
     93 </p>
     94 <ol>
     95 <li>Detect whether an input event occurred.</li>
     96 <li>Identify the input source and its device ID.</li>
     97 <li>Based on the action indicated by the input event key code or axis value,
     98     update the player avatar associated with that device ID.</li>
     99 <li>Render and update the user interface.</li>
    100 </ol>
    101 <p>{@link android.view.KeyEvent} and {@link android.view.MotionEvent} input
    102 events have device IDs associated with them. Your game can take advantage of
    103 this to determine which controller the input event came from, and update the
    104 player avatar associated with that controller.
    105 </p>
    106 <p>The following code snippet shows how you might get a player avatar reference
    107 corresponding to a game controller device ID, and update the game based on the
    108 user's button press on that controller.
    109 </p>
    110 <pre>
    111 &#64;Override
    112 public boolean onKeyDown(int keyCode, KeyEvent event) {
    113     if ((event.getSource() &amp; InputDevice.SOURCE_GAMEPAD)
    114                 == InputDevice.SOURCE_GAMEPAD) {
    115         int deviceId = event.getDeviceId();
    116         if (deviceId != -1) {
    117             Ship currentShip = getShipForId(deviceId);
    118             // Based on which key was pressed, update the player avatar
    119             // (e.g. set the ship headings or fire lasers)
    120             ...
    121             return true;
    122         }
    123     }
    124     return super.onKeyDown(keyCode, event);
    125 }
    126 </pre>
    127 <p class="note"><strong>Note: </strong>As a best practice, when a user's
    128 game controller disconnects, you should pause the game and ask if the user
    129 wants to reconnect.
    130 </p>
    131