am 5020a873: Merge "cherrypick from jb-mr2-docs docs: Training class for game controllers. Change-Id: I697770aee8604c965c3730691459c1e8f10705da" into klp-docs
* commit '5020a8736f3319d295d31bed421f837788878451': cherrypick from jb-mr2-docs docs: Training class for game controllers. Change-Id: I697770aee8604c965c3730691459c1e8f10705da
This commit is contained in:
BIN
docs/downloads/training/ControllerSample.zip
Normal file
BIN
docs/downloads/training/ControllerSample.zip
Normal file
Binary file not shown.
BIN
docs/html/images/training/backward-compatible-inputmanager.png
Normal file
BIN
docs/html/images/training/backward-compatible-inputmanager.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
docs/html/images/training/game-controller-profiles.png
Normal file
BIN
docs/html/images/training/game-controller-profiles.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
643
docs/html/training/game-controllers/compatibility.jd
Normal file
643
docs/html/training/game-controllers/compatibility.jd
Normal file
@@ -0,0 +1,643 @@
|
||||
page.title=Supporting Controllers Across Android Versions
|
||||
trainingnavtop=true
|
||||
|
||||
@jd:body
|
||||
|
||||
<!-- This is the training bar -->
|
||||
<div id="tb-wrapper">
|
||||
<div id="tb">
|
||||
|
||||
<h2>This lesson teaches you to</h2>
|
||||
<ol>
|
||||
<li><a href="#prepare">Prepare to Abstract APIs for Game Controller
|
||||
Suppport</a></li>
|
||||
<li><a href="#abstraction">Add an Interface for Backward Compatibility</a></li>
|
||||
<li><a href="#newer">Implement the Interface on Android 4.1 and Higher</a></li>
|
||||
<li><a href="#older">Implement the Interface on Android 2.3 up to Android
|
||||
4.0</a></li>
|
||||
<li><a href="#using">Use the Version-Specific Implementations</a></li>
|
||||
</ol>
|
||||
|
||||
<h2>Try it out</h2>
|
||||
<div class="download-box">
|
||||
<a href="http://developer.android.com/shareables/training/ControllerSample.zip"
|
||||
class="button">Download the sample</a>
|
||||
<p class="filename">ControllerSample.zip</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>If you are supporting game controllers in your game, it's your responsibility
|
||||
to make sure that your game responds to controllers consistently across devices
|
||||
running on different versions of Android. This lets your game reach a wider
|
||||
audience, and your players can enjoy a seamless gameplay experience with
|
||||
their controllers even when they switch or upgrade their Android devices.</p>
|
||||
|
||||
<p>This lesson demonstrates how to use APIs available in Android 4.1 and higher
|
||||
in a backward compatible way, enabling your game to support the following
|
||||
features on devices running Android 2.3 and higher:</p>
|
||||
<ul>
|
||||
<li>The game can detect if a new game controller is added, changed, or removed.</li>
|
||||
<li>The game can query the capabilities of a game controller.</li>
|
||||
<li>The game can recognize incoming motion events from a game controller.</li>
|
||||
</ul>
|
||||
|
||||
<p>The examples in this lesson are based on the reference implementation
|
||||
provided by the sample {@code ControllerSample.zip} available for download
|
||||
above. This sample shows how to implement the {@code InputManagerCompat}
|
||||
interface to support different versions of Android. To compile the sample, you
|
||||
must use Android 4.1 (API level 16) or higher. Once compiled, the sample app
|
||||
runs on any device running Android 2.3 (API level 9) or higher as the build
|
||||
target.
|
||||
</p>
|
||||
|
||||
<h2 id="prepare">Prepare to Abstract APIs for Game Controller Support</h2>
|
||||
<p>Suppose you want to be able to determine if a game controller's connection
|
||||
status has changed on devices running on Android 2.3 (API level 9). However,
|
||||
the APIs are only available in Android 4.1 (API level 16) and higher, so you
|
||||
need to provide an implementation that supports Android 4.1 and higher while
|
||||
providing a fallback mechanism that supports Android 2.3 up to Android 4.0.</p>
|
||||
|
||||
<p>To help you determine which features require such a fallback mechanism for
|
||||
older versions, table 1 lists the differences in game controller support
|
||||
between Android 2.3 (API level 9), 3.1 (API level 12), and 4.1 (API level
|
||||
16).</p>
|
||||
|
||||
<p class="table-caption" id="game-controller-support-table">
|
||||
<strong>Table 1.</strong> APIs for game controller support across
|
||||
different Android versions.
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Controller Information</th>
|
||||
<th>Controller API</th>
|
||||
<th>API level 9</th>
|
||||
<th>API level 12</th>
|
||||
<th>API level 16</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td rowspan="5">Device Identification</td>
|
||||
<td>{@link android.hardware.input.InputManager#getInputDeviceIds()}</td>
|
||||
<td style="text-align: center;"><big> </big></td>
|
||||
<td style="text-align: center;"><big> </big></td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>{@link android.hardware.input.InputManager#getInputDevice(int)
|
||||
getInputDevice()}</td>
|
||||
<td style="text-align: center;"> </td>
|
||||
<td style="text-align: center;"><big> </big></td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>{@link android.view.InputDevice#getVibrator()}</td>
|
||||
<td style="text-align: center;"> </td>
|
||||
<td style="text-align: center;"><big> </big></td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
</tr>
|
||||
|
||||
<td>{@link android.view.InputDevice#SOURCE_JOYSTICK}</td>
|
||||
<td style="text-align: center;"> </td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>{@link android.view.InputDevice#SOURCE_GAMEPAD}</td>
|
||||
<td style="text-align: center;"> </td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td rowspan="3">Connection Status</td>
|
||||
<td>{@link android.hardware.input.InputManager.InputDeviceListener#onInputDeviceAdded(int) onInputDeviceAdded()}</td>
|
||||
<td style="text-align: center;"> </td>
|
||||
<td style="text-align: center;"> </td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>{@link android.hardware.input.InputManager.InputDeviceListener#onInputDeviceChanged(int) onInputDeviceChanged()}</td>
|
||||
<td style="text-align: center;"> </td>
|
||||
<td style="text-align: center;"> </td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>{@link android.hardware.input.InputManager.InputDeviceListener#onInputDeviceRemoved(int) onInputDeviceRemoved()}</td>
|
||||
<td style="text-align: center;"> </td>
|
||||
<td style="text-align: center;"> </td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td rowspan="4">Input Event Identification</td>
|
||||
<td>D-pad press (
|
||||
{@link android.view.KeyEvent#KEYCODE_DPAD_UP},
|
||||
{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN},
|
||||
{@link android.view.KeyEvent#KEYCODE_DPAD_LEFT},
|
||||
{@link android.view.KeyEvent#KEYCODE_DPAD_RIGHT},
|
||||
{@link android.view.KeyEvent#KEYCODE_DPAD_CENTER})</td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Gamepad button press (
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A},
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B},
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBL BUTTON_THUMBL},
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBR BUTTON_THUMBR},
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT},
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START},
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_R1 BUTTON_R1},
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_L1 BUTTON_L1},
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_R2 BUTTON_R2},
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_L2 BUTTON_L2})</td>
|
||||
<td style="text-align: center;"> </td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Joystick and hat switch movement (
|
||||
{@link android.view.MotionEvent#AXIS_X},
|
||||
{@link android.view.MotionEvent#AXIS_Y},
|
||||
{@link android.view.MotionEvent#AXIS_Z},
|
||||
{@link android.view.MotionEvent#AXIS_RZ},
|
||||
{@link android.view.MotionEvent#AXIS_HAT_X},
|
||||
{@link android.view.MotionEvent#AXIS_HAT_Y})</td>
|
||||
<td style="text-align: center;"> </td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Analog trigger press (
|
||||
{@link android.view.MotionEvent#AXIS_LTRIGGER},
|
||||
{@link android.view.MotionEvent#AXIS_RTRIGGER})</td>
|
||||
<td style="text-align: center;"> </td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
<td style="text-align: center;"><big>•</big></td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>You can use abstraction to build version-aware game controller support that
|
||||
works across platforms. This approach involves the following steps:</p>
|
||||
<ol>
|
||||
<li>Define an intermediary Java interface that abstracts the implementation of
|
||||
the game controller features required by your game.</li>
|
||||
<li>Create a proxy implementation of your interface that uses APIs in Android
|
||||
4.1 and higher.</li>
|
||||
<li>Create a custom implementation of your interface that uses APIs available
|
||||
between Android 2.3 up to Android 4.0.</li>
|
||||
<li>Create the logic for switching between these implementations at runtime,
|
||||
and begin using the interface in your game.</li>
|
||||
</ol>
|
||||
|
||||
<p>For an overview of how abstraction can be used to ensure that applications
|
||||
can work in a backward compatible way across different versions of Android, see
|
||||
<a href="{@docRoot}training/backward-compatible-ui/index.html">Creating
|
||||
Backward-Compatible UIs</a>.
|
||||
</p>
|
||||
|
||||
<h2 id="abstraction">Add an Interface for Backward Compatibility</h2>
|
||||
|
||||
<p>To provide backward compatibility, you can create a custom interface then
|
||||
add version-specific implementations. One advantage of this approach is that it
|
||||
lets you mirror the public interfaces on Android 4.1 (API level 16) that
|
||||
support game controllers.</p>
|
||||
<pre>
|
||||
// The InputManagerCompat interface is a reference example.
|
||||
// The full code is provided in the ControllerSample.zip sample.
|
||||
public interface InputManagerCompat {
|
||||
...
|
||||
public InputDevice getInputDevice(int id);
|
||||
public int[] getInputDeviceIds();
|
||||
|
||||
public void registerInputDeviceListener(
|
||||
InputManagerCompat.InputDeviceListener listener,
|
||||
Handler handler);
|
||||
public void unregisterInputDeviceListener(
|
||||
InputManagerCompat.InputDeviceListener listener);
|
||||
|
||||
public void onGenericMotionEvent(MotionEvent event);
|
||||
|
||||
public void onPause();
|
||||
public void onResume();
|
||||
|
||||
public interface InputDeviceListener {
|
||||
void onInputDeviceAdded(int deviceId);
|
||||
void onInputDeviceChanged(int deviceId);
|
||||
void onInputDeviceRemoved(int deviceId);
|
||||
}
|
||||
...
|
||||
}
|
||||
</pre>
|
||||
<p>The {@code InputManagerCompat} interface provides the following methods:</p>
|
||||
<dl>
|
||||
<dt>{@code getInputDevice()}</dt>
|
||||
<dd>Mirrors {@link android.hardware.input.InputManager#getInputDevice(int)
|
||||
getInputDevice()}. Obtains the {@link android.view.InputDevice}
|
||||
object that represents the capabilities of a game controller.</dd>
|
||||
<dt>{@code getInputDeviceIds()}</dt>
|
||||
<dd>Mirrors {@link android.hardware.input.InputManager#getInputDeviceIds()
|
||||
getInputDeviceIds()}. Returns an array of integers, each of
|
||||
which is an ID for a different input device. This is useful if you're building
|
||||
a game that supports multiple players and you want to detect how many
|
||||
controllers are connected.</dd>
|
||||
<dt>{@code registerInputDeviceListener()}</dt>
|
||||
<dd>Mirrors {@link android.hardware.input.InputManager#registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler)
|
||||
registerInputDeviceListener()}. Lets you register to be informed when a new
|
||||
device is added, changed, or removed.</dd>
|
||||
<dt>{@code unregisterInputDeviceListener()}</dt>
|
||||
<dd>Mirrors {@link android.hardware.input.InputManager#unregisterInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener) unregisterInputDeviceListener()}.
|
||||
Unregisters an input device listener.</dd>
|
||||
<dt>{@code onGenericMotionEvent()}</dt>
|
||||
<dd>Mirrors {@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
|
||||
onGenericMotionEvent()}. Lets your game intercept and handle
|
||||
{@link android.view.MotionEvent} objects and axis values that represent events
|
||||
such as joystick movements and analog trigger presses.</dd>
|
||||
<dt>{@code onPause()}</dt>
|
||||
<dd>Stops polling for game controller events when the
|
||||
main activity is paused, or when the game no longer has focus.</dd>
|
||||
<dt>{@code onResume()}</dt>
|
||||
<dd>Starts polling for game controller events when the
|
||||
main activity is resumed, or when the game is started and runs in the
|
||||
foreground.</dd>
|
||||
<dt>{@code InputDeviceListener}</dt>
|
||||
<dd>Mirrors the {@link android.hardware.input.InputManager.InputDeviceListener}
|
||||
interface. Lets your game know when a game controller has been added, changed, or
|
||||
removed.</dd>
|
||||
</dl>
|
||||
<p>Next, create implementations for {@code InputManagerCompat} that work
|
||||
across different platform versions. If your game is running on Android 4.1 or
|
||||
higher and calls an {@code InputManagerCompat} method, the proxy implementation
|
||||
calls the equivalent method in {@link android.hardware.input.InputManager}.
|
||||
However, if your game is running on Android 2.3 up to Android 4.0, the custom implementation processes calls to {@code InputManagerCompat} methods by using
|
||||
only APIs introduced no later than Android 2.3. Regardless of which
|
||||
version-specific implementation is used at runtime, the implementation passes
|
||||
the call results back transparently to the game.</p>
|
||||
|
||||
<img src="{@docRoot}images/training/backward-compatible-inputmanager.png" alt=""
|
||||
id="figure1" />
|
||||
<p class="img-caption">
|
||||
<strong>Figure 1.</strong> Class diagram of interface and version-specific
|
||||
implementations.
|
||||
</p>
|
||||
|
||||
<h2 id="newer">Implement the Interface on Android 4.1 and Higher</h2>
|
||||
<p>{@code InputManagerCompatV16} is an implementation of the
|
||||
{@code InputManagerCompat} interface that proxies method calls to an
|
||||
actual {@link android.hardware.input.InputManager} and {@link
|
||||
android.hardware.input.InputManager.InputDeviceListener}. The
|
||||
{@link android.hardware.input.InputManager} is obtained from the system
|
||||
{@link android.content.Context}.</p>
|
||||
|
||||
<pre>
|
||||
// The InputManagerCompatV16 class is a reference implementation.
|
||||
// The full code is provided in the ControllerSample.zip sample.
|
||||
public class InputManagerV16 implements InputManagerCompat {
|
||||
|
||||
private final InputManager mInputManager;
|
||||
private final Map<InputManagerCompat.InputDeviceListener,
|
||||
V16InputDeviceListener> mListeners;
|
||||
|
||||
public InputManagerV16(Context context) {
|
||||
mInputManager = (InputManager)
|
||||
context.getSystemService(Context.INPUT_SERVICE);
|
||||
mListeners = new HashMap<InputManagerCompat.InputDeviceListener,
|
||||
V16InputDeviceListener>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputDevice getInputDevice(int id) {
|
||||
return mInputManager.getInputDevice(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getInputDeviceIds() {
|
||||
return mInputManager.getInputDeviceIds();
|
||||
}
|
||||
|
||||
static class V16InputDeviceListener implements
|
||||
InputManager.InputDeviceListener {
|
||||
final InputManagerCompat.InputDeviceListener mIDL;
|
||||
|
||||
public V16InputDeviceListener(InputDeviceListener idl) {
|
||||
mIDL = idl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputDeviceAdded(int deviceId) {
|
||||
mIDL.onInputDeviceAdded(deviceId);
|
||||
}
|
||||
|
||||
// Do the same for device change and removal
|
||||
...
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerInputDeviceListener(InputDeviceListener listener,
|
||||
Handler handler) {
|
||||
V16InputDeviceListener v16Listener = new
|
||||
V16InputDeviceListener(listener);
|
||||
mInputManager.registerInputDeviceListener(v16Listener, handler);
|
||||
mListeners.put(listener, v16Listener);
|
||||
}
|
||||
|
||||
// Do the same for unregistering an input device listener
|
||||
...
|
||||
|
||||
@Override
|
||||
public void onGenericMotionEvent(MotionEvent event) {
|
||||
// unused in V16
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
// unused in V16
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
// unused in V16
|
||||
}
|
||||
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h2 id="older">Implementing the Interface on Android 2.3 up to Android 4.0</h2>
|
||||
|
||||
<p>The {@code InputManagerV9} implementation uses APIs introduced no later
|
||||
than Android 2.3. To create an implementation of {@code
|
||||
InputManagerCompat} that supports Android 2.3 up to Android 4.0, you can use
|
||||
the following objects:
|
||||
<ul>
|
||||
<li>A {@link android.util.SparseArray} of device IDs to track the
|
||||
game controllers that are connected to the device.</li>
|
||||
<li>A {@link android.os.Handler} to process device events. When an app is started
|
||||
or resumed, the {@link android.os.Handler} receives a message to start polling
|
||||
for game controller disconnection. The {@link android.os.Handler} will start a
|
||||
loop to check each known connected game controller and see if a device ID is
|
||||
returned. A {@code null} return value indicates that the game controller is
|
||||
disconnected. The {@link android.os.Handler} stops polling when the app is
|
||||
paused.</li>
|
||||
<li>A {@link java.util.Map} of {@code InputManagerCompat.InputDeviceListener}
|
||||
objects. You will use the listeners to update the connection status of tracked
|
||||
game controllers.</li>
|
||||
</ul>
|
||||
|
||||
<pre>
|
||||
// The InputManagerCompatV9 class is a reference implementation.
|
||||
// The full code is provided in the ControllerSample.zip sample.
|
||||
public class InputManagerV9 implements InputManagerCompat {
|
||||
private final SparseArray<long[]> mDevices;
|
||||
private final Map<InputDeviceListener, Handler> mListeners;
|
||||
private final Handler mDefaultHandler;
|
||||
…
|
||||
|
||||
public InputManagerV9() {
|
||||
mDevices = new SparseArray<long[]>();
|
||||
mListeners = new HashMap<InputDeviceListener, Handler>();
|
||||
mDefaultHandler = new PollingMessageHandler(this);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Implement a {@code PollingMessageHandler} object that extends
|
||||
{@link android.os.Handler}, and override the
|
||||
{@link android.os.Handler#handleMessage(android.os.Message) handleMessage()}
|
||||
method. This method checks if an attached game controller has been
|
||||
disconnected and notifies registered listeners.</p>
|
||||
|
||||
<pre>
|
||||
private static class PollingMessageHandler extends Handler {
|
||||
private final WeakReference<InputManagerV9> mInputManager;
|
||||
|
||||
PollingMessageHandler(InputManagerV9 im) {
|
||||
mInputManager = new WeakReference<InputManagerV9>(im);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
super.handleMessage(msg);
|
||||
switch (msg.what) {
|
||||
case MESSAGE_TEST_FOR_DISCONNECT:
|
||||
InputManagerV9 imv = mInputManager.get();
|
||||
if (null != imv) {
|
||||
long time = SystemClock.elapsedRealtime();
|
||||
int size = imv.mDevices.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
long[] lastContact = imv.mDevices.valueAt(i);
|
||||
if (null != lastContact) {
|
||||
if (time - lastContact[0] > CHECK_ELAPSED_TIME) {
|
||||
// check to see if the device has been
|
||||
// disconnected
|
||||
int id = imv.mDevices.keyAt(i);
|
||||
if (null == InputDevice.getDevice(id)) {
|
||||
// Notify the registered listeners
|
||||
// that the game controller is disconnected
|
||||
...
|
||||
imv.mDevices.remove(id);
|
||||
} else {
|
||||
lastContact[0] = time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT,
|
||||
CHECK_ELAPSED_TIME);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>To start and stop polling for game controller disconnection, override
|
||||
these methods:</p>
|
||||
<pre>
|
||||
private static final int MESSAGE_TEST_FOR_DISCONNECT = 101;
|
||||
private static final long CHECK_ELAPSED_TIME = 3000L;
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
mDefaultHandler.removeMessages(MESSAGE_TEST_FOR_DISCONNECT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
mDefaultHandler.sendEmptyMessageDelayed(MESSAGE_TEST_FOR_DISCONNECT,
|
||||
CHECK_ELAPSED_TIME);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>To detect that an input device has been added, override the
|
||||
{@code onGenericMotionEvent()} method. When the system reports a motion event,
|
||||
check if this event came from a device ID that is already tracked, or from a
|
||||
new device ID. If the device ID is new, notify registered listeners.</p>
|
||||
|
||||
<pre>
|
||||
@Override
|
||||
public void onGenericMotionEvent(MotionEvent event) {
|
||||
// detect new devices
|
||||
int id = event.getDeviceId();
|
||||
long[] timeArray = mDevices.get(id);
|
||||
if (null == timeArray) {
|
||||
// Notify the registered listeners that a game controller is added
|
||||
...
|
||||
timeArray = new long[1];
|
||||
mDevices.put(id, timeArray);
|
||||
}
|
||||
long time = SystemClock.elapsedRealtime();
|
||||
timeArray[0] = time;
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Notification of listeners is implemented by using the
|
||||
{@link android.os.Handler} object to send a {@code DeviceEvent}
|
||||
{@link java.lang.Runnable} object to the message queue. The {@code DeviceEvent}
|
||||
contains a reference to an {@code InputManagerCompat.InputDeviceListener}. When
|
||||
the {@code DeviceEvent} runs, the appropriate callback method of the listener
|
||||
is called to signal if the game controller was added, changed, or removed.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
@Override
|
||||
public void registerInputDeviceListener(InputDeviceListener listener,
|
||||
Handler handler) {
|
||||
mListeners.remove(listener);
|
||||
if (handler == null) {
|
||||
handler = mDefaultHandler;
|
||||
}
|
||||
mListeners.put(listener, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterInputDeviceListener(InputDeviceListener listener) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
private void notifyListeners(int why, int deviceId) {
|
||||
// the state of some device has changed
|
||||
if (!mListeners.isEmpty()) {
|
||||
for (InputDeviceListener listener : mListeners.keySet()) {
|
||||
Handler handler = mListeners.get(listener);
|
||||
DeviceEvent odc = DeviceEvent.getDeviceEvent(why, deviceId,
|
||||
listener);
|
||||
handler.post(odc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class DeviceEvent implements Runnable {
|
||||
private int mMessageType;
|
||||
private int mId;
|
||||
private InputDeviceListener mListener;
|
||||
private static Queue<DeviceEvent> sObjectQueue =
|
||||
new ArrayDeque<DeviceEvent>();
|
||||
...
|
||||
|
||||
static DeviceEvent getDeviceEvent(int messageType, int id,
|
||||
InputDeviceListener listener) {
|
||||
DeviceEvent curChanged = sObjectQueue.poll();
|
||||
if (null == curChanged) {
|
||||
curChanged = new DeviceEvent();
|
||||
}
|
||||
curChanged.mMessageType = messageType;
|
||||
curChanged.mId = id;
|
||||
curChanged.mListener = listener;
|
||||
return curChanged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
switch (mMessageType) {
|
||||
case ON_DEVICE_ADDED:
|
||||
mListener.onInputDeviceAdded(mId);
|
||||
break;
|
||||
case ON_DEVICE_CHANGED:
|
||||
mListener.onInputDeviceChanged(mId);
|
||||
break;
|
||||
case ON_DEVICE_REMOVED:
|
||||
mListener.onInputDeviceRemoved(mId);
|
||||
break;
|
||||
default:
|
||||
// Handle unknown message type
|
||||
...
|
||||
break;
|
||||
}
|
||||
// Put this runnable back in the queue
|
||||
sObjectQueue.offer(this);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>You now have two implementations of {@code InputManagerCompat}: one that
|
||||
works on devices running Android 4.1 and higher, and another
|
||||
that works on devices running Android 2.3 up to Android 4.0.</p>
|
||||
|
||||
<h2 id="using">Use the Version-Specific Implementation</h2>
|
||||
<p>The version-specific switching logic is implemented in a class that acts as
|
||||
a <a href="http://en.wikipedia.org/wiki/Factory_(software_concept)"
|
||||
class="external-link" target="_blank">factory</a>.</p>
|
||||
<pre>
|
||||
public static class Factory {
|
||||
public static InputManagerCompat getInputManager(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
return new InputManagerV16(context);
|
||||
} else {
|
||||
return new InputManagerV9();
|
||||
}
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
<p>Now you can simply instantiate an {@code InputManagerCompat} object and
|
||||
register an {@code InputManagerCompat.InputDeviceListener} in your main
|
||||
{@link android.view.View}. Because of the version-switching logic you set
|
||||
up, your game automatically uses the implementation that's appropriate for the
|
||||
version of Android the device is running.</p>
|
||||
<pre>
|
||||
public class GameView extends View implements InputDeviceListener {
|
||||
private InputManagerCompat mInputManager;
|
||||
...
|
||||
|
||||
public GameView(Context context, AttributeSet attrs) {
|
||||
mInputManager =
|
||||
InputManagerCompat.Factory.getInputManager(this.getContext());
|
||||
mInputManager.registerInputDeviceListener(this, null);
|
||||
...
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
<p>Next, override the
|
||||
{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
|
||||
onGenericMotionEvent()} method in your main view, as described in
|
||||
<a href="controller-input.html#analog">Handle a MotionEvent from a Game
|
||||
Controller</a>. Your game should now be able to process game controller events
|
||||
consistently on devices running Android 2.3 (API level 9) and higher.
|
||||
<p>
|
||||
<pre>
|
||||
@Override
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
mInputManager.onGenericMotionEvent(event);
|
||||
|
||||
// Handle analog input from the controller as normal
|
||||
...
|
||||
return super.onGenericMotionEvent(event);
|
||||
}
|
||||
</pre>
|
||||
<p>You can find a complete implementation of this compatibility code in the
|
||||
{@code GameView} class provided in the sample {@code ControllerSample.zip}
|
||||
available for download above.</p>
|
||||
656
docs/html/training/game-controllers/controller-input.jd
Normal file
656
docs/html/training/game-controllers/controller-input.jd
Normal file
@@ -0,0 +1,656 @@
|
||||
page.title=Handling Controller Actions
|
||||
trainingnavtop=true
|
||||
|
||||
@jd:body
|
||||
|
||||
<!-- This is the training bar -->
|
||||
<div id="tb-wrapper">
|
||||
<div id="tb">
|
||||
|
||||
<h2>This lesson teaches you to</h2>
|
||||
<ol>
|
||||
<li><a href="#input">Verify a Game Controller is Connected</a></li>
|
||||
<li><a href="#button">Process Gamepad Button Presses</a>
|
||||
</li>
|
||||
<li><a href="#dpad">Process Directional Pad Input</a>
|
||||
</li>
|
||||
<li><a href="#joystick">Process Joystick Movements</a>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h2>Try it out</h2>
|
||||
<div class="download-box">
|
||||
<a href="http://developer.android.com/shareables/training/ControllerSample.zip"
|
||||
class="button">Download the sample</a>
|
||||
<p class="filename">ControllerSample.zip</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>At the system level, Android reports input event codes from game controllers
|
||||
as Android key codes and axis values. In your game, you can receive these codes
|
||||
and values and convert them to specific in-game actions.</p>
|
||||
|
||||
<p>When players physically connect or wirelessly pair a game controller to
|
||||
their Android-powered devices, the system auto-detects the controller
|
||||
as an input device and starts reporting its input events. Your game can receive
|
||||
these input events by implementing the following callback methods in your active
|
||||
{@link android.app.Activity} or focused {@link android.view.View} (you should
|
||||
implement the callbacks for either the {@link android.app.Activity} or
|
||||
{@link android.view.View}, but not both): </p>
|
||||
|
||||
<ul>
|
||||
<li>From {@link android.app.Activity}:
|
||||
<ul>
|
||||
<li>{@link android.app.Activity#dispatchGenericMotionEvent(android.view.MotionEvent) dispatchGenericMotionEvent(android.view.MotionEvent)}
|
||||
<p>Called to process generic motion events such as joystick movements.</p>
|
||||
</li>
|
||||
<li>{@link android.app.Activity#dispatchKeyEvent(android.view.KeyEvent) dispatchKeyEvent(android.view.KeyEvent)}
|
||||
<p>Called to process key events such as a press or release of a
|
||||
gamepad or D-pad button.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>From {@link android.view.View}:
|
||||
<ul>
|
||||
<li>{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
|
||||
onGenericMotionEvent(android.view.MotionEvent)}
|
||||
<p>Called to process generic motion events such as joystick movements.</p>
|
||||
</li>
|
||||
<li>{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown(int, android.view.KeyEvent)}
|
||||
<p>Called to process a press of a physical key such as a gamepad or
|
||||
D-pad button.</p>
|
||||
</li>
|
||||
<li>{@link android.view.View#onKeyUp(int, android.view.KeyEvent) onKeyUp(int, android.view.KeyEvent)}
|
||||
<p>Called to process a release of a physical key such as a gamepad or
|
||||
D-pad button.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>The recommended approach is to capture the events from the
|
||||
specific {@link android.view.View} object that the user interacts with.
|
||||
Inspect the following objects provided by the callbacks to get information
|
||||
about the type of input event received:</p>
|
||||
|
||||
<dl>
|
||||
<dt>{@link android.view.KeyEvent}</dt>
|
||||
<dd>An object that describes directional
|
||||
pad</a> (D-pad) and gamepad button events. Key events are accompanied by a
|
||||
<em>key code</em> that indicates the specific button triggered, such as
|
||||
{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN DPAD_DOWN}
|
||||
or {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}. You can obtain the
|
||||
key code by calling {@link android.view.KeyEvent#getKeyCode()} or from key
|
||||
event callbacks such as
|
||||
{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()}.
|
||||
<dd>
|
||||
<dt>{@link android.view.MotionEvent}</dt>
|
||||
<dd>An object that describes input from joystick and shoulder trigger
|
||||
movements. Motion events are accompanied by an action code and a set of
|
||||
<em>axis values</em>. The action code specifies the state change that occurred
|
||||
such as a joystick being moved. The axis values describe the position and other
|
||||
movement properties for a specific physical control, such as
|
||||
{@link android.view.MotionEvent#AXIS_X} or
|
||||
{@link android.view.MotionEvent#AXIS_RTRIGGER}. You can obtain the action code
|
||||
by calling {@link android.view.MotionEvent#getAction()} and the axis value by
|
||||
calling {@link android.view.MotionEvent#getAxisValue(int) getAxisValue()}.
|
||||
<dd>
|
||||
</dl>
|
||||
<p>This lesson focuses on how you can handle input from the most common types of
|
||||
physical controls (gamepad buttons, directional pads, and
|
||||
joysticks) in a game screen by implementing the above-mentioned
|
||||
{@link android.view.View} callback methods and processing
|
||||
{@link android.view.KeyEvent} and {@link android.view.MotionEvent} objects.</p>
|
||||
|
||||
<h2 id="input">Verify a Game Controller is Connected</h2>
|
||||
<p>When reporting input events, Android does not distinguish
|
||||
between events that came from a non-game controller device and events that came
|
||||
from a game controller. For example, a touch screen action generates an
|
||||
{@link android.view.MotionEvent#AXIS_X} event that represents the X
|
||||
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
|
||||
your game cares about handling game-controller input, you should first check
|
||||
that the input event comes from a relevant source type.</p>
|
||||
<p>To verify that a connected input device is a game controller, call
|
||||
{@link android.view.InputDevice#getSources()} to obtain a combined bit field of
|
||||
input source types supported on that device. You can then test to see if
|
||||
the following fields are set:</p>
|
||||
<ul>
|
||||
<li>A source type of {@link android.view.InputDevice#SOURCE_GAMEPAD} indicates
|
||||
that the input device has gamepad buttons (for example,
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}). Note that this source
|
||||
type does not strictly indicate if the game controller has D-pad buttons,
|
||||
although most gamepads typically have directional controls.</li>
|
||||
<li>A source type of {@link android.view.InputDevice#SOURCE_DPAD} indicates that
|
||||
the input device has D-pad buttons (for example,
|
||||
{@link android.view.KeyEvent#KEYCODE_DPAD_UP DPAD_UP}).</li>
|
||||
<li>A source type of {@link android.view.InputDevice#SOURCE_JOYSTICK}
|
||||
indicates that the input device has analog control sticks (for example, a
|
||||
joystick that records movements along {@link android.view.MotionEvent#AXIS_X}
|
||||
and {@link android.view.MotionEvent#AXIS_Y}).</li>
|
||||
</ul>
|
||||
<p>The following code snippet shows a helper method that lets you check whether
|
||||
the connected input devices are game controllers. If so, the method retrieves
|
||||
the device IDs for the game controllers. You can then associate each device
|
||||
ID with a player in your game, and process game actions for each connected
|
||||
player separately. To learn more about supporting multiple game controllers
|
||||
that are simultaneously connected on the same Android device, see
|
||||
<a href="multiple-controllers.html">Supporting Multiple Game Controllers</a>.</p>
|
||||
<pre>
|
||||
public ArrayList<Integer> getGameControllerIds() {
|
||||
ArrayList<Integer> gameControllerDeviceIds = new ArrayList<Integer>();
|
||||
int[] deviceIds = InputDevice.getDeviceIds();
|
||||
for (int deviceId : deviceIds) {
|
||||
InputDevice dev = InputDevice.getDevice(deviceId);
|
||||
int sources = dev.getSources();
|
||||
|
||||
// Verify that the device has gamepad buttons, control sticks, or both.
|
||||
if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
|
||||
|| ((sources & InputDevice.SOURCE_JOYSTICK)
|
||||
== InputDevice.SOURCE_JOYSTICK)) {
|
||||
// This device is a game controller. Store its device ID.
|
||||
if (!gameControllerDeviceIds.contains(deviceId)) {
|
||||
gameControllerDeviceIds.add(deviceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return gameControllerDeviceIds;
|
||||
}
|
||||
</pre>
|
||||
<p>Additionally, you might want to check for individual input capabilities
|
||||
supported by a connected game controller. This might be useful, for example, if
|
||||
you want your game to use only input from the set of physical controls it
|
||||
understands.</p>
|
||||
<p>To detect if a specific key code or axis code is supported by a connected
|
||||
game controller, use these techniques:</p>
|
||||
<ul>
|
||||
<li>In Android 4.4 (API level 19) or higher, you can determine if a key code is
|
||||
supported on a connected game controller by calling
|
||||
{@link android.view.InputDevice#hasKeys(int...)}.</li>
|
||||
<li>In Android 3.1 (API level 12) or higher, you can find all available axes
|
||||
supported on a connected game controller by first calling
|
||||
{@link android.view.InputDevice#getMotionRanges()}. Then, on each
|
||||
{@link android.view.InputDevice.MotionRange} object returned, call
|
||||
{@link android.view.InputDevice.MotionRange#getAxis()} to get its axis ID.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="button">Process Gamepad Button Presses</h2>
|
||||
<p>Figure 1 shows how Android maps key codes and axis values to the physical
|
||||
controls on most game controllers.</p>
|
||||
<img src="{@docRoot}images/training/game-controller-profiles.png" alt=""
|
||||
id="figure1" />
|
||||
<p class="img-caption">
|
||||
<strong>Figure 1.</strong> Profile for a generic game controller.
|
||||
</p>
|
||||
<p>The callouts in the figure refer to the following:</p>
|
||||
<div style="-moz-column-count:2;-webkit-column-count:2;column-count:2;">
|
||||
<ol style="margin-left:30px;list-style:decimal;">
|
||||
<li>{@link android.view.MotionEvent#AXIS_HAT_X},
|
||||
{@link android.view.MotionEvent#AXIS_HAT_Y},
|
||||
{@link android.view.KeyEvent#KEYCODE_DPAD_UP DPAD_UP},
|
||||
{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN DPAD_DOWN},
|
||||
{@link android.view.KeyEvent#KEYCODE_DPAD_LEFT DPAD_LEFT},
|
||||
{@link android.view.KeyEvent#KEYCODE_DPAD_RIGHT DPAD_RIGHT}
|
||||
</li>
|
||||
<li>{@link android.view.MotionEvent#AXIS_X},
|
||||
{@link android.view.MotionEvent#AXIS_Y},
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBL BUTTON_THUMBL}</li>
|
||||
<li>{@link android.view.MotionEvent#AXIS_Z},
|
||||
{@link android.view.MotionEvent#AXIS_RZ},
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_THUMBR BUTTON_THUMBR}</li>
|
||||
<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_X BUTTON_X}</li>
|
||||
<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}</li>
|
||||
<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_Y BUTTON_Y}</li>
|
||||
<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}</li>
|
||||
<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_R1 BUTTON_R1}</li>
|
||||
<li>{@link android.view.MotionEvent#AXIS_RTRIGGER},
|
||||
{@link android.view.MotionEvent#AXIS_THROTTLE}</li>
|
||||
<li>{@link android.view.MotionEvent#AXIS_LTRIGGER},
|
||||
{@link android.view.MotionEvent#AXIS_BRAKE}</li>
|
||||
<li>{@link android.view.KeyEvent#KEYCODE_BUTTON_L1 BUTTON_L1}</li>
|
||||
</ol>
|
||||
</div>
|
||||
<p>Common key codes generated by gamepad button presses include
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A},
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B},
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT},
|
||||
and {@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START}. Some game
|
||||
controllers also trigger the {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER
|
||||
DPAD_CENTER} key code when the center of the D-pad crossbar is pressed. Your
|
||||
game can inspect the key code by calling {@link android.view.KeyEvent#getKeyCode()}
|
||||
or from key event callbacks such as
|
||||
{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()},
|
||||
and if it represents an event that is relevant to your game, process it as a
|
||||
game action. Table 1 lists the recommended game actions for the most common
|
||||
gamepad buttons.
|
||||
</p>
|
||||
|
||||
<p class="table-caption" id="table1">
|
||||
<strong>Table 1.</strong> Recommended game actions for gamepad
|
||||
buttons.</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th scope="col">Game Action</th>
|
||||
<th scope="col">Button Key Code</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Start game in main menu, or pause/unpause during game</td>
|
||||
<td>{@link android.view.KeyEvent#KEYCODE_BUTTON_START BUTTON_START}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Display menu</td>
|
||||
<td>{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT} and
|
||||
{@link android.view.KeyEvent#KEYCODE_MENU}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Same as Android <em>Back</em></td>
|
||||
<td>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}<sup>*</sup> and
|
||||
{@link android.view.KeyEvent#KEYCODE_BACK KEYCODE_BACK}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Confirm selection, or perform primary game action</td>
|
||||
<td>{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}<sup>*</sup> and
|
||||
{@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
<em>* This could be the opposite button (A/B), depending on the locale that
|
||||
you are supporting.</em>
|
||||
</p>
|
||||
|
||||
<p class="note"><strong>Tip: </strong>Consider providing a configuration screen
|
||||
in your game to allow users to personalize their own game controller mappings for
|
||||
game actions.</p>
|
||||
|
||||
<p>The following snippet shows how you might override
|
||||
{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()} to
|
||||
associate the {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} and
|
||||
{@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER} button presses
|
||||
with a game action.
|
||||
</p>
|
||||
<pre>
|
||||
public class GameView extends View {
|
||||
...
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
boolean handled = false;
|
||||
if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)
|
||||
== InputDevice.SOURCE_GAMEPAD) {
|
||||
if (event.getRepeatCount() == 0) {
|
||||
switch (keyCode) {
|
||||
// Handle gamepad and D-pad button presses to
|
||||
// navigate the ship
|
||||
...
|
||||
|
||||
default:
|
||||
if (isFireKey(keyCode)) {
|
||||
// Update the ship object to fire lasers
|
||||
...
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (handled) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
private static boolean isFireKey(int keyCode) {
|
||||
// Here we treat Button_A and DPAD_CENTER as the primary action
|
||||
// keys for the game. You may need to switch this to Button_B and
|
||||
// DPAD_CENTER depending on the user expectations for the locale
|
||||
// in which your game runs.
|
||||
return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
|
||||
|| keyCode == KeyEvent.KEYCODE_BUTTON_A;
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Follow these best practices when handling button presses:</p>
|
||||
<ul>
|
||||
<li><strong>Provide localized button mappings.</strong> Generally, if your game
|
||||
has a primary gameplay action (for example, it fires lasers, lets your avatar
|
||||
do a high jump, or confirms an item selection), you should map
|
||||
both {@link android.view.KeyEvent#KEYCODE_DPAD_CENTER DPAD_CENTER} and
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} to this action. However,
|
||||
in some locales, users may expect
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B} to be the confirm
|
||||
button and {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} to be the
|
||||
back button instead. If you are supporting these locales, make sure to treat
|
||||
the A and B buttons accordingly in your game. To determine the user's locale,
|
||||
call the {@link java.util.Locale#getDefault()} method.
|
||||
<li><strong>Map {@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A}
|
||||
consistently across different Android versions.</strong> On Android 4.2 (API
|
||||
level 17) and lower, the system treats
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} as the Android
|
||||
<em>Back</em> key by default. If your app supports these Android
|
||||
versions, make sure to treat
|
||||
{@link android.view.KeyEvent#KEYCODE_BUTTON_A BUTTON_A} as the primary game
|
||||
action (except in the localization case mentioned
|
||||
above). To determine the current Android SDK
|
||||
version on the device, refer to the
|
||||
{@link android.os.Build.VERSION#SDK_INT Build.VERSION.SDK_INT} value.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="dpad">Process Directional Pad Input</h2>
|
||||
<p>The 4-way directional pad (D-pad) is a common physical control in many game
|
||||
controllers. Android reports D-pad UP and DOWN presses as
|
||||
{@link android.view.MotionEvent#AXIS_HAT_Y} events with a range
|
||||
from -1.0 (up) to 1.0 (down), and D-pad LEFT or RIGHT presses as
|
||||
{@link android.view.MotionEvent#AXIS_HAT_X} events with a range from -1.0
|
||||
(left) to 1.0 (right).</p>
|
||||
<p>Some controllers instead report D-pad presses with a key code. If your game
|
||||
cares about D-pad presses, you should treat the hat axis events and the D-pad
|
||||
key codes as the same input events, as recommended in table 2.</p>
|
||||
<p class="table-caption" id="table2">
|
||||
<strong>Table 2.</strong> Recommended default game actions for D-pad key
|
||||
codes and hat axis values.</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th scope="col">Game Action</th>
|
||||
<th scope="col">D-pad Key Code</th>
|
||||
<th scope="col">Hat Axis Code</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Move Up</td>
|
||||
<td>{@link android.view.KeyEvent#KEYCODE_DPAD_UP}</td>
|
||||
<td>{@link android.view.MotionEvent#AXIS_HAT_Y} (for values 0 to -1.0)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Move Down</td>
|
||||
<td>{@link android.view.KeyEvent#KEYCODE_DPAD_DOWN}</td>
|
||||
<td>{@link android.view.MotionEvent#AXIS_HAT_Y} (for values 0 to 1.0)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Move Left</td>
|
||||
<td>{@link android.view.KeyEvent#KEYCODE_DPAD_LEFT}</td>
|
||||
<td>{@link android.view.MotionEvent#AXIS_HAT_X} (for values 0 to -1.0)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Move Right</td>
|
||||
<td>{@link android.view.KeyEvent#KEYCODE_DPAD_RIGHT}</td>
|
||||
<td>{@link android.view.MotionEvent#AXIS_HAT_X} (for values 0 to 1.0)</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<p>The following code snippet shows a helper class that lets you check the hat
|
||||
axis and key code values from an input event to determine the D-pad direction.
|
||||
</p>
|
||||
<pre>
|
||||
public class Dpad {
|
||||
final static int UP = 0;
|
||||
final static int LEFT = 1;
|
||||
final static int RIGHT = 2;
|
||||
final static int DOWN = 3;
|
||||
final static int CENTER = 4;
|
||||
|
||||
int directionPressed = -1; // initialized to -1
|
||||
|
||||
public int getDirectionPressed(InputEvent event) {
|
||||
if (!isDpadDevice(event)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If the input event is a MotionEvent, check its hat axis values.
|
||||
if (event instanceof MotionEvent) {
|
||||
|
||||
// Use the hat axis value to find the D-pad direction
|
||||
MotionEvent motionEvent = (MotionEvent) event;
|
||||
float xaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X);
|
||||
float yaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y);
|
||||
|
||||
// Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
|
||||
// LEFT and RIGHT direction accordingly.
|
||||
if (Float.compare(xaxis, -1.0f) == 0) {
|
||||
directionPressed = Dpad.LEFT;
|
||||
} else if (Float.compare(xaxis, 1.0f) == 0) {
|
||||
directionPressed = Dpad.RIGHT;
|
||||
}
|
||||
// Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
|
||||
// UP and DOWN direction accordingly.
|
||||
else if (Float.compare(yaxis, -1.0f) == 0) {
|
||||
directionPressed = Dpad.UP;
|
||||
} else if (Float.compare(yaxis, -1.0f) == 0) {
|
||||
directionPressed = Dpad.DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// If the input event is a KeyEvent, check its key code.
|
||||
else if (event instanceof KeyEvent) {
|
||||
|
||||
// Use the key code to find the D-pad direction.
|
||||
KeyEvent keyEvent = (KeyEvent) event;
|
||||
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
|
||||
directionPressed = Dpad.LEFT;
|
||||
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
|
||||
directionPressed = Dpad.RIGHT;
|
||||
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
|
||||
directionPressed = Dpad.UP;
|
||||
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
|
||||
directionPressed = Dpad.DOWN;
|
||||
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||
directionPressed = Dpad.CENTER;
|
||||
}
|
||||
}
|
||||
return directionPressed;
|
||||
}
|
||||
|
||||
public static boolean isDpadDevice(InputEvent event) {
|
||||
// Check that input comes from a device with directional pads.
|
||||
if ((event.getSource() & InputDevice.SOURCE_DPAD)
|
||||
!= InputDevice.SOURCE_DPAD) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>You can use this helper class in your game wherever you want to process
|
||||
D-pad input (for example, in the
|
||||
{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
|
||||
onGenericMotionEvent()} or
|
||||
{@link android.view.View#onKeyDown(int, android.view.KeyEvent) onKeyDown()}
|
||||
callbacks).</p>
|
||||
<p>For example:</p>
|
||||
<pre>
|
||||
Dpad mDpad = new Dpad();
|
||||
...
|
||||
@Override
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
|
||||
// Check if this event if from a D-pad and process accordingly.
|
||||
if (Dpad.isDpadDevice(event)) {
|
||||
|
||||
int press = mDpad.getDirectionPressed(event);
|
||||
switch (press) {
|
||||
case LEFT:
|
||||
// Do something for LEFT direction press
|
||||
...
|
||||
return true;
|
||||
case RIGHT:
|
||||
// Do something for RIGHT direction press
|
||||
...
|
||||
return true;
|
||||
case UP:
|
||||
// Do something for UP direction press
|
||||
...
|
||||
return true;
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this event is from a joystick movement and process accordingly.
|
||||
...
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h2 id="joystick">Process Joystick Movements</h2>
|
||||
<p>When players move a joystick on their game controllers, Android reports a
|
||||
{@link android.view.MotionEvent} that contains the
|
||||
{@link android.view.MotionEvent#ACTION_MOVE} action code and the updated
|
||||
positions of the joystick's axes. Your game can use the data provided by
|
||||
the {@link android.view.MotionEvent} to determine if a joystick movement it
|
||||
cares about happened.
|
||||
</p>
|
||||
<p>Note that joystick motion events may batch multiple movement samples together
|
||||
within a single object. The {@link android.view.MotionEvent} object contains
|
||||
the current position for each joystick axis as well as multiple historical
|
||||
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
|
||||
axis values for efficiency. The historical values for an axis consists of the
|
||||
set of distinct values older than the current axis value, and more recent than
|
||||
values reported in any previous motion events. See the
|
||||
{@link android.view.MotionEvent} reference for details.</p>
|
||||
<p>You can use the historical information to more accurately render a game
|
||||
object's movement based on the joystick input. To
|
||||
retrieve the current and historical values, call
|
||||
{@link android.view.MotionEvent#getAxisValue(int)
|
||||
getAxisValue()} or {@link android.view.MotionEvent#getHistoricalAxisValue(int,
|
||||
int) getHistoricalAxisValue()}. You can also find the number of historical
|
||||
points in the joystick event by calling
|
||||
{@link android.view.MotionEvent#getHistorySize()}.</p>
|
||||
<p>The following snippet shows how you might override the
|
||||
{@link android.view.View#onGenericMotionEvent(android.view.MotionEvent)
|
||||
onGenericMotionEvent()} callback to process joystick input. You should first
|
||||
process the historical values for an axis, then process its current position.
|
||||
</p>
|
||||
<pre>
|
||||
public class GameView extends View {
|
||||
|
||||
@Override
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
|
||||
// Check that the event came from a game controller
|
||||
if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) ==
|
||||
InputDevice.SOURCE_JOYSTICK &&
|
||||
event.getAction() == MotionEvent.ACTION_MOVE)
|
||||
|
||||
// Process all historical movement samples in the batch
|
||||
final int historySize = event.getHistorySize();
|
||||
|
||||
// Process the movements starting from the
|
||||
// earliest historical position in the batch
|
||||
for (int i = 0; i < historySize; i++) {
|
||||
// Process the event at historical position i
|
||||
processJoystickInput(event, i);
|
||||
}
|
||||
|
||||
// Process the current movement sample in the batch (position -1)
|
||||
processJoystickInput(event, -1);
|
||||
return true;
|
||||
}
|
||||
return super.onGenericMotionEvent(event);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
<p>Before using joystick input, you need to determine if the joystick is
|
||||
centered, then calculate its axis movements accordingly. Joysticks typically
|
||||
have a <em>flat</em> area, that is, a range of values near the (0,0) coordinate
|
||||
at which the axis is considered to be centered. If the axis value reported by
|
||||
Android falls within the flat area, you should treat the controller to be at
|
||||
rest (that is, motionless along both axes).</p>
|
||||
<p>The snippet below shows a helper method that calculates the movement along
|
||||
each axis. You invoke this helper in the {@code processJoystickInput()} method
|
||||
described further below.
|
||||
</p>
|
||||
<pre>
|
||||
private static float getCenteredAxis(MotionEvent event,
|
||||
InputDevice device, int axis, int historyPos) {
|
||||
final InputDevice.MotionRange range =
|
||||
device.getMotionRange(axis, event.getSource());
|
||||
|
||||
// A joystick at rest does not always report an absolute position of
|
||||
// (0,0). Use the getFlat() method to determine the range of values
|
||||
// bounding the joystick axis center.
|
||||
if (range != null) {
|
||||
final float flat = range.getFlat();
|
||||
final float value =
|
||||
historyPos < 0 ? event.getAxisValue(axis):
|
||||
event.getHistoricalAxisValue(axis, historyPos);
|
||||
|
||||
// Ignore axis values that are within the 'flat' region of the
|
||||
// joystick axis center.
|
||||
if (Math.abs(value) > flat) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
</pre>
|
||||
<p>Putting it all together, here is how you might process joystick movements in
|
||||
your game:</p>
|
||||
<pre>
|
||||
private void processJoystickInput(MotionEvent event,
|
||||
int historyPos) {
|
||||
|
||||
InputDevice mInputDevice = event.getDevice();
|
||||
|
||||
// Calculate the horizontal distance to move by
|
||||
// using the input value from one of these physical controls:
|
||||
// the left control stick, hat axis, or the right control stick.
|
||||
float x = getCenteredAxis(event, mInputDevice,
|
||||
MotionEvent.AXIS_X, historyPos);
|
||||
if (x == 0) {
|
||||
x = getCenteredAxis(event, mInputDevice,
|
||||
MotionEvent.AXIS_HAT_X, historyPos);
|
||||
}
|
||||
if (x == 0) {
|
||||
x = getCenteredAxis(event, mInputDevice,
|
||||
MotionEvent.AXIS_Z, historyPos);
|
||||
}
|
||||
|
||||
// Calculate the vertical distance to move by
|
||||
// using the input value from one of these physical controls:
|
||||
// the left control stick, hat switch, or the right control stick.
|
||||
float y = getCenteredAxis(event, mInputDevice,
|
||||
MotionEvent.AXIS_Y, historyPos);
|
||||
if (y == 0) {
|
||||
y = getCenteredAxis(event, mInputDevice,
|
||||
MotionEvent.AXIS_HAT_Y, historyPos);
|
||||
}
|
||||
if (y == 0) {
|
||||
y = getCenteredAxis(event, mInputDevice,
|
||||
MotionEvent.AXIS_RZ, historyPos);
|
||||
}
|
||||
|
||||
// Update the ship object based on the new x and y values
|
||||
...
|
||||
|
||||
return true;
|
||||
}
|
||||
</pre>
|
||||
<p>To support game controllers that have more sophisticated
|
||||
features beyond a single joystick, follow these best practices: </p>
|
||||
<ul>
|
||||
<li><strong>Handle dual controller sticks.</strong> Many game controllers have
|
||||
both a left and right joystick. For the left stick, Android
|
||||
reports horizontal movements as {@link android.view.MotionEvent#AXIS_X} events
|
||||
and vertical movements as {@link android.view.MotionEvent#AXIS_Y} events.
|
||||
For the right stick, Android reports horizontal movements as
|
||||
{@link android.view.MotionEvent#AXIS_Z} events and vertical movements as
|
||||
{@link android.view.MotionEvent#AXIS_RZ} events. Make sure to handle
|
||||
both controller sticks in your code.</li>
|
||||
<li><strong>Handle shoulder trigger presses (but provide alternative input
|
||||
methods).</strong> Some controllers have left and right shoulder
|
||||
triggers. If these triggers are present, Android reports a left trigger press
|
||||
as an {@link android.view.MotionEvent#AXIS_LTRIGGER} event and a
|
||||
right trigger press as an
|
||||
{@link android.view.MotionEvent#AXIS_RTRIGGER} event. On Android
|
||||
4.3 (API level 18), a controller that produces a
|
||||
{@link android.view.MotionEvent#AXIS_LTRIGGER} also reports an
|
||||
identical value for the {@link android.view.MotionEvent#AXIS_BRAKE} axis. The
|
||||
same is true for {@link android.view.MotionEvent#AXIS_RTRIGGER} and
|
||||
{@link android.view.MotionEvent#AXIS_GAS}. Android reports all analog trigger
|
||||
presses with a normalized value from 0.0 (released) to 1.0 (fully pressed). Not
|
||||
all controllers have triggers, so consider allowing players to perform those
|
||||
game actions with other buttons.
|
||||
</li>
|
||||
</ul>
|
||||
60
docs/html/training/game-controllers/index.jd
Normal file
60
docs/html/training/game-controllers/index.jd
Normal file
@@ -0,0 +1,60 @@
|
||||
page.title=Supporting Game Controllers
|
||||
page.tags="game controller"
|
||||
|
||||
trainingnavtop=true
|
||||
startpage=true
|
||||
|
||||
@jd:body
|
||||
|
||||
<div id="tb-wrapper">
|
||||
<div id="tb">
|
||||
|
||||
<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
|
||||
<h2>Dependencies and prerequisites</h2>
|
||||
<ul>
|
||||
<li>Android 2.3 (API level 9) or higher.</li>
|
||||
</ul>
|
||||
|
||||
<h2>You should also read</h2>
|
||||
<ul>
|
||||
<li><a
|
||||
href="http://source.android.com/devices/tech/input/key-layout-files.html"
|
||||
class="external-link" target="_blank">Key Layout Files</a></li>
|
||||
<li><a href="{@docRoot}guide/topics/ui/ui-events.html">Input Events</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>Try it out</h2>
|
||||
<div class="download-box">
|
||||
<a href="http://developer.android.com/shareables/training/ControllerSample.zip"
|
||||
class="button">Download the sample</a>
|
||||
<p class="filename">ControllerSample.zip</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>You can greatly enhance the user experience in your game by letting
|
||||
players use their favorite game controllers. The Android framework provides
|
||||
APIs for detecting and processing user input from game controllers.</p>
|
||||
|
||||
<p>This class shows how to make your game work consistently with game
|
||||
controllers across different Android API levels (API level 9 and up),
|
||||
and how to enhance the gaming experience for players by supporting multiple
|
||||
controllers simultaneously in your app.</p>
|
||||
|
||||
<h2>Lessons</h2>
|
||||
|
||||
<dl>
|
||||
<dt><b><a href="controller-input.html">Handling Controller
|
||||
Actions</a></b></dt>
|
||||
<dd>Learn how to handle user input from common
|
||||
input elements on game controllers, including directional pad (D-pad)
|
||||
buttons, gamepad buttons, and joysticks.</dd>
|
||||
<dt><b><a href="compatibility.html">Supporting Controllers Across Android
|
||||
Versions</a></b></dt>
|
||||
<dd>Learn how to make game controllers behave the same across
|
||||
devices running different versions of Android.</dd>
|
||||
<dt><b><a href="multiple-controllers.html">Supporting Multiple Game
|
||||
Controllers</a></b></dt>
|
||||
<dd>Learn how to detect and use multiple game controllers that
|
||||
are simultaneously connected.</dd>
|
||||
130
docs/html/training/game-controllers/multiple-controllers.jd
Normal file
130
docs/html/training/game-controllers/multiple-controllers.jd
Normal file
@@ -0,0 +1,130 @@
|
||||
page.title=Supporting Multiple Game Controllers
|
||||
trainingnavtop=true
|
||||
|
||||
@jd:body
|
||||
|
||||
<!-- This is the training bar -->
|
||||
<div id="tb-wrapper">
|
||||
<div id="tb">
|
||||
|
||||
<h2>This lesson teaches you to</h2>
|
||||
<ol>
|
||||
<li><a href="#map">Map Players to Controller Device IDs</a></li>
|
||||
<li><a href="#detect">Process Multiple Controller Input</a></li>
|
||||
</ol>
|
||||
|
||||
<h2>Try it out</h2>
|
||||
<div class="download-box">
|
||||
<a href="http://developer.android.com/shareables/training/ControllerSample.zip"
|
||||
class="button">Download the sample</a>
|
||||
<p class="filename">ControllerSample.zip</p>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<p>While most games are designed to support a single user per Android device,
|
||||
it's also possible to support multiple users with game controllers that are
|
||||
connected simultaneously on the same Android device.</p>
|
||||
<p>This lesson covers some basic techniques for handling input in your single
|
||||
device multiplayer game from multiple connected controllers. This includes
|
||||
maintaining a mapping between player avatars and each controller device and
|
||||
processing controller input events appropriately.
|
||||
</p>
|
||||
|
||||
<h2 id="map">Map Players to Controller Device IDs</h2>
|
||||
<p>When a game controller is connected to an Android device, the system
|
||||
assigns it an integer device ID. You can obtain the device IDs for connected
|
||||
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
|
||||
device ID with a player in your game, and process game actions for each player separately.
|
||||
</p>
|
||||
<p class="note"><strong>Note: </strong>On devices running Android 4.1 (API
|
||||
level 16) and higher, you can obtain an input device’s descriptor using
|
||||
{@link android.view.InputDevice#getDescriptor()}, which returns a unique
|
||||
persistent string value for the input device. Unlike a device ID, the descriptor
|
||||
value won't change even if the input device is disconnected, reconnected, or
|
||||
reconfigured.
|
||||
</p>
|
||||
<p>The code snippet below shows how to use a {@link android.util.SparseArray}
|
||||
to associate a player's avatar with a specific controller. In this example, the
|
||||
{@code mShips} variable stores a collection of {@code Ship} objects. A new
|
||||
player avatar is created in-game when a new controller is attached by a user,
|
||||
and removed when its associated controller is removed.
|
||||
</p>
|
||||
<p>The {@code onInputDeviceAdded()} and {@code onInputDeviceRemoved()} callback
|
||||
methods are part of the abstraction layer introduced in
|
||||
<a href="{@docRoot}training/game-controllers/compatibility.html#status_callbacks}">
|
||||
Supporting Controllers Across Android Versions</a>. By implementing these
|
||||
listener callbacks, your game can identify the game controller's device ID when a
|
||||
controller is added or removed. This detection is compatible with Android 2.3
|
||||
(API level 9) and higher.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
private final SparseArray<Ship> mShips = new SparseArray<Ship>();
|
||||
|
||||
@Override
|
||||
public void onInputDeviceAdded(int deviceId) {
|
||||
getShipForID(deviceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputDeviceRemoved(int deviceId) {
|
||||
removeShipForID(deviceId);
|
||||
}
|
||||
|
||||
private Ship getShipForID(int shipID) {
|
||||
Ship currentShip = mShips.get(shipID);
|
||||
if ( null == currentShip ) {
|
||||
currentShip = new Ship();
|
||||
mShips.append(shipID, currentShip);
|
||||
}
|
||||
return currentShip;
|
||||
}
|
||||
|
||||
private void removeShipForID(int shipID) {
|
||||
mShips.remove(shipID);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h2 id="detect">Process Multiple Controller Input</h2>
|
||||
<p>Your game should execute the following loop to process
|
||||
input from multiple controllers:
|
||||
</p>
|
||||
<ol>
|
||||
<li>Detect whether an input event occurred.</li>
|
||||
<li>Identify the input source and its device ID.</li>
|
||||
<li>Based on the action indicated by the input event key code or axis value,
|
||||
update the player avatar associated with that device ID.</li>
|
||||
<li>Render and update the user interface.</li>
|
||||
</ol>
|
||||
<p>{@link android.view.KeyEvent} and {@link android.view.MotionEvent} input
|
||||
events have device IDs associated with them. Your game can take advantage of
|
||||
this to determine which controller the input event came from, and update the
|
||||
player avatar associated with that controller.
|
||||
</p>
|
||||
<p>The following code snippet shows how you might get a player avatar reference
|
||||
corresponding to a game controller device ID, and update the game based on the
|
||||
user's button press on that controller.
|
||||
</p>
|
||||
<pre>
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)
|
||||
== InputDevice.SOURCE_GAMEPAD) {
|
||||
int deviceId = event.getDeviceId();
|
||||
if (deviceId != -1) {
|
||||
Ship currentShip = getShipForId(deviceId);
|
||||
// Based on which key was pressed, update the player avatar
|
||||
// (e.g. set the ship headings or fire lasers)
|
||||
...
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
</pre>
|
||||
<p class="note"><strong>Note: </strong>As a best practice, when a user's
|
||||
game controller disconnects, you should pause the game and ask if the user
|
||||
wants to reconnect.
|
||||
</p>
|
||||
@@ -1,6 +1,4 @@
|
||||
<ul id="nav">
|
||||
|
||||
|
||||
<li class="nav-section">
|
||||
<div class="nav-section-header">
|
||||
<a href="<?cs var:toroot ?>training/index.html">
|
||||
@@ -1116,6 +1114,29 @@ results."
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-section">
|
||||
<div class="nav-section-header">
|
||||
<a href="<?cs var:toroot ?>training/game-controllers/index.html"
|
||||
description=
|
||||
"How to write apps that support game controllers."
|
||||
>Supporting Game Controllers</a>
|
||||
</div>
|
||||
<ul>
|
||||
<li><a href="<?cs var:toroot ?>training/game-controllers/controller-input.html">
|
||||
Handling Controller Actions
|
||||
</a>
|
||||
</li>
|
||||
<li><a href="<?cs var:toroot ?>training/game-controllers/compatibility.html">
|
||||
Supporting Controllers Across Android Versions
|
||||
</a>
|
||||
</li>
|
||||
<li><a href="<?cs var:toroot ?>training/game-controllers/multiple-controllers.html">
|
||||
Supporting Multiple Game Controllers
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li> <!-- end of User Input -->
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user