663 lines
28 KiB
Plaintext
663 lines
28 KiB
Plaintext
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}<sup>*</sup></td>
|
|
</tr>
|
|
<tr>
|
|
<td>Display menu</td>
|
|
<td>{@link android.view.KeyEvent#KEYCODE_BUTTON_SELECT BUTTON_SELECT}<sup>*</sup>
|
|
and {@link android.view.KeyEvent#KEYCODE_MENU}<sup>*</sup></td>
|
|
</tr>
|
|
<tr>
|
|
<td>Same as Android <em>Back</em></td>
|
|
<td>{@link android.view.KeyEvent#KEYCODE_BACK KEYCODE_BACK}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Navigate back to a previous item in a menu</td>
|
|
<td>{@link android.view.KeyEvent#KEYCODE_BUTTON_B BUTTON_B}<sup>**</sup></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>* Your game should not rely on the presence of the Start, Select, or Menu
|
|
buttons.</em>
|
|
</p>
|
|
<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> |