کنترلکنندهها دو نوع کنش دارند:
KeyEventبرای هر دکمهای با وضعیت دودویی «روشن» و «خاموش» استفاده میشودMotionEventبرای هر محوری که محدودهای از مقادیر را برمیگرداند استفاده میشود. برای مثال، از -۱ تا ۱ برای دسته آنالوگ یا از ۰ تا ۱ برای ماشههای آنالوگ.
میتوانید این ورودیها را از View که focus دارد بخوانید.
onGenericMotionEventبرای هرMotionEventافزایش مییابد.onKeyDownوonKeyUpبرایKeyEventوقتی دکمهها فشار داده و رها میشوند بالا میآیند.
کاتلین
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (event.isFromSource(SOURCE_GAMEPAD)
&& event.repeatCount == 0
) {
Log.d("GameView", "Gamepad key pressed: $keyCode")
return true
}
return super.onKeyDown(keyCode, event)
}
override fun onGenericMotionEvent(event: MotionEvent): Boolean {
if (event.isFromSource(SOURCE_JOYSTICK)) {
Log.d("GameView", "Gamepad event: $event")
return true
}
return super.onGenericMotionEvent(event)
}
جاوا
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (event.isFromSource(SOURCE_GAMEPAD)
&& event.getRepeatCount() == 0
) {
Log.d("GameView", "Gamepad key pressed: " + keyCode);
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if (event.isFromSource(SOURCE_JOYSTICK)) {
Log.d("GameView", "Gamepad event: " + event);
return true;
}
return super.onGenericMotionEvent(event);
}
درصورت نیاز، میتوانید رویدادها را مستقیماً از Activity بخوانید.
dispatchGenericMotionEventبرای هرMotionEventافزایش مییابدdispatchKeyEvent. برای هرKeyEventافزایش مییابد.
درستیسنجی کنید کنترلکننده بازی متصل باشد
هنگام گزارش رویدادهای ورودی، Android از همان کلید یا شناسههای محور برای انواع مختلف دستگاه ورودی استفاده مجدد میکند. برای مثال، کنش صفحه لمسی رویداد AXIS_X را تولید میکند که نشاندهنده مختصات X سطح لمسی است، اما گیمپد رویداد AXIS_X را تولید میکند که نشاندهنده موقعیت X چوبک سمت چپ است. این یعنی باید نوع منبع را بررسی کنید تا
رویدادهای ورودی را بهدرستی تفسیر کنید.
برای تأیید اینکه InputDevice متصلشده کنترلکننده بازی است، از تابع
supportsSource(int) استفاده کنید:
- نوع منبع
SOURCE_GAMEPADنشان میدهد که دستگاه ورودی دکمههای کنترلکننده دارد (برای مثال،KEYCODE_BUTTON_A). توجه داشته باشید که این نوع منبع بهطور دقیق نشان نمیدهد که کنترل بازی دکمههای پد کنترل دارد یا نه، اگرچه اکثر کنترلکنندهها معمولاً کنترلهای جهتی دارند. - نوع منبع
SOURCE_DPADنشان میدهد که دستگاه ورودی دکمههای پد کنترل دارد (برای مثال،DPAD_UP). - نوع منبع
SOURCE_JOYSTICKنشان میدهد که دستگاه ورودی دارای دستههای کنترل آنالوگ است (برای مثال، دسته بازی که حرکات را در امتدادAXIS_XوAXIS_Yثبت میکند).
تکه کد زیر روش کمکی را نشان میدهد که به شما امکان میدهد بررسی کنید آیا دستگاههای ورودی متصل کنترلکننده بازی هستند یا نه. در این صورت، روش شناسههای دستگاه را برای کنترلکنندههای بازی بازیابی میکند. سپس میتوانید هر شناسه دستگاه را با بازیکنی در بازیتان مرتبط کنید و کنشهای بازی را برای هر بازیکن متصل بهصورت جداگانه پردازش کنید. برای کسب اطلاعات بیشتر درباره پشتیبانی از چند دسته کنترل بازی که بهطور همزمان به یک دستگاه Android متصل هستند، به پشتیبانی از چند دسته کنترل بازی مراجعه کنید.
کاتلین
fun getGameControllerIds(): List<Int> {
val gameControllerDeviceIds = mutableListOf<Int>()
val deviceIds = InputDevice.getDeviceIds()
deviceIds.forEach { deviceId ->
InputDevice.getDevice(deviceId)?.apply {
// Verify that the device has gamepad buttons, control sticks, or both.
if (supportsSource(SOURCE_GAMEPAD)
|| supportsSource(SOURCE_JOYSTICK)) {
// This device is a game controller. Store its device ID.
gameControllerDeviceIds
.takeIf { !it.contains(deviceId) }
?.add(deviceId)
}
}
}
return gameControllerDeviceIds
}
جاوا
public ArrayList<Integer> getGameControllerIds() {
ArrayList<Integer> gameControllerDeviceIds = new ArrayList<Integer>();
int[] deviceIds = InputDevice.getDeviceIds();
for (int deviceId : deviceIds) {
InputDevice dev = InputDevice.getDevice(deviceId);
if (dev == null) {
continue;
}
// Verify that the device has gamepad buttons, control sticks, or both.
if (dev.supportsSource(SOURCE_GAMEPAD) || dev.supportsSource(SOURCE_JOYSTICK)) {
// This device is a game controller. Store its device ID.
if (!gameControllerDeviceIds.contains(deviceId)) {
gameControllerDeviceIds.add(deviceId);
}
}
}
return gameControllerDeviceIds;
}
ورودیهای کنترلکننده پردازش
این بخش انواع کنترلکنندههای بازی را که در Android پشتیبانی میشوند شرح میدهد.
توسعهدهندگان C++ باید از کتابخانه کنترلکننده بازی استفاده کنند. این ویژگی همه کنترلکنندهها را در رایجترین زیرمجموعه ویژگیها یکپارچه میکند و یک رابط کاربری یکپارچه بین آنها ارائه میدهد، ازجمله قابلیت تشخیص چیدمان دکمه.
این شکل نشان میدهد که توسعهدهنده بازی Android میتواند انتظار داشته باشد کنترلکننده معمولی در Android چگونه باشد.
این جدول نامها و انواع رویدادهای استاندارد را برای کنترلکنندههای بازی فهرست میکند. برای
فهرست کامل رویدادها، گونههای رایج را ببینید. سیستم MotionEvent رویداد را ازطریق onGenericMotionEvent و KeyEvent رویداد را ازطریق onKeyDown و onKeyUp ارسال میکند.
| ورودی کنترلکننده | KeyEvent | MotionEvent |
|---|---|---|
| ۱. پد کنترل |
AXIS_HAT_X(ورودی افقی) AXIS_HAT_Y(ورودی عمودی) |
|
| ۲. دسته کنترل آنالوگ چپ |
KEYCODE_BUTTON_THUMBL(وقتی فشرده میشود) |
AXIS_X(حرکت افقی) AXIS_Y(حرکت عمودی) |
| ۳. دسته کنترل آنالوگ راست |
KEYCODE_BUTTON_THUMBR(وقتی فشرده میشود) |
AXIS_Z(حرکت افقی) AXIS_RZ(حرکت عمودی) |
| ۴. دکمه X | KEYCODE_BUTTON_X |
|
| ۵. دکمه A | KEYCODE_BUTTON_A |
|
| ۶. دکمه Y | KEYCODE_BUTTON_Y |
|
| ۷. دکمه B | KEYCODE_BUTTON_B |
|
| ۸. دکمه بامپر راست |
KEYCODE_BUTTON_R1 |
|
| ۹. راهانداز راست |
AXIS_RTRIGGER |
|
| ۱۰. راهانداز چپ | AXIS_LTRIGGER |
|
| ۱۱. دکمه بامپر چپ | KEYCODE_BUTTON_L1 |
|
| ۱۲. شروع | KEYCODE_BUTTON_START |
|
| ۱۳. انتخاب | KEYCODE_BUTTON_SELECT |
مدیریت فشار دکمهها
ازآنجاییکه Android فشار دکمه کنترلکننده را دقیقاً مشابه فشار دکمه صفحهکلید گزارش میکند، باید:
- تأیید کنید که رویداد از
SOURCE_GAMEPADمیآید. - مطمئن شوید که دکمه را فقط یکبار با
KeyEvent.getRepeatCount()دریافت میکنید، Android رویدادهای کلیدی تکراری را درست مثل زمانی که کلید صفحهکلید را پایین نگه داشتهاید ارسال میکند. - با برگرداندن
trueنشان دهید که رویدادی مدیریت میشود. رویدادهای مدیریتنشده را به
superارسال کنید تا مطمئن شوید لایههای سازگاری مختلف Android بهدرستی کار میکنند.کاتلین
class GameView : View { // ... override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { event.apply { var handled = false // make sure we're handling gamepad events if (isFromSource(SOURCE_GAMEPAD)) { // avoid processing the keycode repeatedly if (repeatCount == 0) { when (keyCode) { // handle the "A" button KEYCODE_BUTTON_A -> { handled = true } } // ... } } if (handled) { return true } } return super.onKeyDown(keyCode, event) } }جاوا
public class GameView extends View { // ... @Override public boolean onKeyDown(int keyCode, KeyEvent event) { boolean handled = false; // make sure we're handling gamepad events if (event.isFromSource(SOURCE_GAMEPAD)) { // avoid processing the keycode repeatedly if (event.getRepeatCount() == 0) { switch (keyCode) { case KEYCODE_BUTTON_A: // handle the "A" button handled = true; break; // ... } } // mark this event as handled if (handled) { return true; } } // Always do this instead of "return false" // it allows Android's input compatibility layers to work return super.onKeyDown(keyCode, event); } }
پردازش ورودی پد جهتدار
پد کنترل چهارطرفه یا پد کنترل، یک کنترل فیزیکی رایج در بسیاری از کنترلکنندههای بازی است. Android فشار دکمههای «بالا» و «پایین» صفحه کلید کنترل را بهعنوان رویدادهای AXIS_HAT_Y گزارش میکند،
که در آن ۱- نشاندهنده بالا و ۱ نشاندهنده پایین است. فشارهای «پد کنترل» به «چپ» یا «راست» را بهعنوان رویدادهای AXIS_HAT_X گزارش میکند، که -۱.۰ نشاندهنده چپ و ۱.۰ نشاندهنده راست است.
برخیاز کنترلکنندهها بهجای آن، فشار دکمههای D-pad را با کد کلید گزارش میکنند. اگر بازی شما به فشار دکمههای پد کنترل اهمیت میدهد، باید رویدادهای محور کلاهک و کدهای کلید پد کنترل را بهعنوان رویدادهای ورودی یکسان درنظر بگیرید، همانطور که در جدول ۲ توصیه شده است.
جدول ۲. کنشهای بازی پیشفرض توصیهشده برای کدهای کلید پد کنترل و مقادیر محور کلاهک.
| کنش بازی | کد کلید پد کنترل | کد محور کلاه |
|---|---|---|
| حرکت بهبالا | KEYCODE_DPAD_UP |
AXIS_HAT_Y (برای مقادیر ۰ تا -۱٫۰) |
| حرکت بهپایین | KEYCODE_DPAD_DOWN |
AXIS_HAT_Y (برای مقادیر ۰ تا ۱٫۰) |
| حرکت بهچپ | KEYCODE_DPAD_LEFT |
AXIS_HAT_X (برای مقادیر ۰ تا -۱٫۰) |
| حرکت بهراست | KEYCODE_DPAD_RIGHT |
AXIS_HAT_X (برای مقادیر ۰ تا ۱٫۰) |
تکهکد زیر کلاس کمکی را نشان میدهد که به شما امکان میدهد مقادیر محور کلاه و کد کلید را از رویداد ورودی بررسی کنید تا جهت پد کنترل را تعیین کنید.
کاتلین
class Dpad {
private var directionPressed = -1 // initialized to -1
fun getDirectionPressed(event: InputEvent): Int {
if (!isDpadDevice(event)) {
return -1
}
// If the input event is a MotionEvent, check its hat axis values.
(event as? MotionEvent)?.apply {
// Use the hat axis value to find the D-pad direction
val xaxis: Float = event.getAxisValue(MotionEvent.AXIS_HAT_X)
val yaxis: Float = event.getAxisValue(MotionEvent.AXIS_HAT_Y)
directionPressed = when {
// Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
// LEFT and RIGHT direction accordingly.
xaxis.compareTo(-1.0f) == 0 -> Dpad.LEFT
xaxis.compareTo(1.0f) == 0 -> Dpad.RIGHT
// Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
// UP and DOWN direction accordingly.
yaxis.compareTo(-1.0f) == 0 -> Dpad.UP
yaxis.compareTo(1.0f) == 0 -> Dpad.DOWN
else -> directionPressed
}
}
// If the input event is a KeyEvent, check its key code.
(event as? KeyEvent)?.apply {
// Use the key code to find the D-pad direction.
directionPressed = when(event.keyCode) {
KeyEvent.KEYCODE_DPAD_LEFT -> Dpad.LEFT
KeyEvent.KEYCODE_DPAD_RIGHT -> Dpad.RIGHT
KeyEvent.KEYCODE_DPAD_UP -> Dpad.UP
KeyEvent.KEYCODE_DPAD_DOWN -> Dpad.DOWN
KeyEvent.KEYCODE_DPAD_CENTER -> Dpad.CENTER
else -> directionPressed
}
}
return directionPressed
}
companion object {
internal const val UP = 0
internal const val LEFT = 1
internal const val RIGHT = 2
internal const val DOWN = 3
internal const val CENTER = 4
fun isDpadDevice(event: InputEvent): Boolean =
// Check that input comes from a device with directional pads.
return event.isFromSource(InputDevice.SOURCE_DPAD)
}
}
جاوا
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.
return event.isFromSource(InputDevice.SOURCE_DPAD);
}
}
هرجا که بخواهید ورودی «کلیدهای کنترل» را در بازیتان پردازش کنید (برای نمونه، در
onGenericMotionEvent()
یا
onKeyDown()
پاسخگوییها)، میتوانید از این کلاس کمکی استفاده کنید.
برای مثال:
کاتلین
private val dpad = Dpad()
...
override fun onGenericMotionEvent(event: MotionEvent): Boolean {
if (Dpad.isDpadDevice(event)) {
when (dpad.getDirectionPressed(event)) {
Dpad.LEFT -> {
// Do something for LEFT direction press
...
return true
}
Dpad.RIGHT -> {
// Do something for RIGHT direction press
...
return true
}
Dpad.UP -> {
// Do something for UP direction press
...
return true
}
...
}
}
// Check if this event is from a joystick movement and process accordingly.
...
}
جاوا
Dpad dpad = 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 = dpad.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.
...
}
پردازش حرکات دسته کنترل
وقتی بازیکنان دسته کنترل بازی را حرکت میدهند، Android
MotionEvent را گزارش میکند که حاوی
ACTION_MOVE کد
عمل و موقعیتهای بهروزشده محورهای دسته کنترل است. بازی شما میتواند از دادههای ارائهشده توسط MotionEvent
برای تعیین اینکه آیا حرکت دستهای که به آن اهمیت میدهد رخ داده است یا نه استفاده کند.
توجه داشته باشید که رویدادهای حرکت جویاستیک ممکن است چندین نمونه حرکت را در یک شیء دستهای کند. شیء
MotionEvent حاوی
موقعیت فعلی برای هر محور جویاستیک و همچنین چندین موقعیت تاریخی
برای هر محور است. هنگام گزارش رویدادهای حرکت با کد کنش
ACTION_MOVE (مثل
حرکات دسته بازی)، Android مقادیر محور را برای کارایی دستهبندی میکند. مقادیر
تاریخی برای یک محور شامل مجموعه مقادیر متمایزی است که قدیمیتر از
مقدار محور فعلی هستند و جدیدتر از مقادیری هستند که در هریک از
رویدادهای حرکتی قبلی گزارش شدهاند. برای جزئیات، به MotionEvent
مرجع مراجعه کنید.
برای ارائه دقیق حرکت یک شیء بازی براساس ورودی دستهبازی، میتوانید
از اطلاعات سابقه ارائهشده توسط اشیاء MotionEvent استفاده کنید.
بااستفاده از روشهای زیر میتوانید مقادیر کنونی و گذشته را بازیابی کنید:
getAxisValue()getHistoricalAxisValue()getHistorySize()(برای یافتن تعداد امتیازهای تاریخی در رویداد دستهبازی)
تکهکد زیر نشان میدهد که چگونه میتوانید
onGenericMotionEvent()
بازخوان را برای پردازش ورودی دستهبازی ملغی کنید. ابتدا باید مقادیر تاریخی محور را پردازش کنید، سپس موقعیت کنونی آن را پردازش کنید.
کاتلین
class GameView(...) : View(...) {
override fun onGenericMotionEvent(event: MotionEvent): Boolean {
// Check that the event came from a game controller
return if (event.source and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK
&& event.action == MotionEvent.ACTION_MOVE) {
// Process the movements starting from the
// earliest historical position in the batch
(0 until event.historySize).forEach { i ->
// Process the event at historical position i
processJoystickInput(event, i)
}
// Process the current movement sample in the batch (position -1)
processJoystickInput(event, -1)
true
} else {
super.onGenericMotionEvent(event)
}
}
}
جاوا
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);
}
}
قبلاز استفاده از ورودی دسته کنترل، باید تعیین کنید که آیا دسته کنترل در مرکز قرار دارد یا نه، سپس حرکتهای محور آن را براساس آن محاسبه کنید. دسته بازی معمولاً دارای یک ناحیه مسطح است، یعنی محدودهای از مقادیر نزدیک به مختصات (۰،۰) که در آن محور بهعنوان مرکز درنظر گرفته میشود. اگر مقدار محور گزارششده توسط Android در منطقه مسطح قرار گیرد، باید کنترلکننده را در حالت استراحت درنظر بگیرید (یعنی بدون حرکت در هر دو محور).
گزیده روش کمکی را نشان میدهد که حرکت را در امتداد هر محور محاسبه میکند. این دستیار را در روش processJoystickInput() که در نمونه زیر بیشتر توضیح داده شده است فرا میخوانید:
کاتلین
private fun getCenteredAxis(
event: MotionEvent,
device: InputDevice,
axis: Int,
historyPos: Int
): Float {
val range: InputDevice.MotionRange? = device.getMotionRange(axis, event.source)
// 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.
range?.apply {
val value: Float = if (historyPos < 0) {
event.getAxisValue(axis)
} else {
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 0f
}
جاوا
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;
}
با کنار هم گذاشتن همه اینها، در اینجا نحوه پردازش حرکات دسته کنترل در بازی شما آورده شده است:
کاتلین
private fun processJoystickInput(event: MotionEvent, historyPos: Int) {
val inputDevice = event.device
// 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.
var x: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_X, historyPos)
if (x == 0f) {
x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_X, historyPos)
}
if (x == 0f) {
x = getCenteredAxis(event, inputDevice, 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.
var y: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Y, historyPos)
if (y == 0f) {
y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_Y, historyPos)
}
if (y == 0f) {
y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_RZ, historyPos)
}
// Update the ship object based on the new x and y values
}
جاوا
private void processJoystickInput(MotionEvent event,
int historyPos) {
InputDevice inputDevice = 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, inputDevice,
MotionEvent.AXIS_X, historyPos);
if (x == 0) {
x = getCenteredAxis(event, inputDevice,
MotionEvent.AXIS_HAT_X, historyPos);
}
if (x == 0) {
x = getCenteredAxis(event, inputDevice,
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, inputDevice,
MotionEvent.AXIS_Y, historyPos);
if (y == 0) {
y = getCenteredAxis(event, inputDevice,
MotionEvent.AXIS_HAT_Y, historyPos);
}
if (y == 0) {
y = getCenteredAxis(event, inputDevice,
MotionEvent.AXIS_RZ, historyPos);
}
// Update the ship object based on the new x and y values
}
برای پشتیبانی از دستههای بازی که ویژگیهای پیچیدهتری فراتر از یک دسته کنترل دارند، این روالهای مطلوب را دنبال کنید:
- دستههای کنترلر دوگانه را کنترل کنید. بسیاری از کنترلکنندههای بازی دارای هر دو جویاستیک چپ و راست هستند. برای چوبک سمت چپ، Android حرکتهای افقی را
بهصورت رویدادهای
AXIS_Xو حرکتهای عمودی را بهصورت رویدادهایAXIS_Yگزارش میکند. برای دسته راست، Android حرکتهای افقی را بهعنوان رویدادهایAXIS_Zو حرکتهای عمودی را بهعنوان رویدادهایAXIS_RZگزارش میکند. مطمئن شوید که هر دو چوبک کنترلر را در کد خود مدیریت کنید. - فشار دکمههای شانهای را مدیریت کنید (و مطمئن شوید که بازیتان با رویدادهای
AXIS_وKEYCODE_BUTTON_کار میکند). برخیاز کنترلکنندهها دارای کلیدهای شانهای چپ و راست هستند. وقتی این راهاندازها وجود داشته باشند، رویدادAXIS_*TRIGGERیاKEYCODE_BUTTON_*2یا هر دو را منتشر میکنند. برای ماشه سمت چپ، اینهاAXIS_LTRIGGERوKEYCODE_BUTTON_L2هستند. برای ماشه راست، اینهاAXIS_RTRIGGERوKEYCODE_BUTTON_R2هستند. رویدادهای محور فقط درصورتی رخ میدهند که راهانداز محدودهای از مقادیر بین ۰ و ۱ را منتشر کند، و برخیاز کنترلکنندههای دارای برونداد آنالوگ علاوه بر رویدادهای محور، رویدادهای دکمهای نیز منتشر میکنند. بازیها باید از هر دو رویدادAXIS_وKEYCODE_BUTTON_پشتیبانی کنند تا با همه دستههای کنترل بازی رایج سازگار بمانند، اما اگر دسته کنترل هر دو رویداد را گزارش کند، رویدادی را ترجیح دهید که برای بازی شما منطقیتر است. در Android 4.3 (سطح API 18) و بالاتر، کنترلکنندهای کهAXIS_LTRIGGERتولید میکند، مقدار یکسانی را برای محورAXIS_BRAKEنیز گزارش میکند. همین امر برایAXIS_RTRIGGERوAXIS_GASنیز صادق است. Android همه فشارهای محرک آنالوگ را با مقدار نرمالسازیشده از ۰٫۰ (رهاشده) تا ۱٫۰ (کاملاً فشرده) گزارش میکند. - عملکردهای خاص و پشتیبانی ممکن است در محیطهای شبیهسازیشده متفاوت باشد.
پلاتفرمهای شبیهسازیشده، مانند بازیهای Google Play،
ممکن است براساس قابلیتهای سیستمعامل میزبان
در عملکرد کمی متفاوت باشند. برای مثال، برخیاز کنترلکنندههایی که هم رویدادهای
AXIS_و همKEYCODE_BUTTON_را منتشر میکنند، فقط رویدادهایAXIS_را منتشر میکنند، و پشتیبانی از برخیاز کنترلکنندهها ممکن است بهطور کامل وجود نداشته باشد.
گونههای رایج
با تنوع گسترده پشتیبانی Android از کنترلکنندهها، ممکن است نحوه ساخت و آزمایش برای تأیید اینکه بازی شما بدون اشکال در میان پایگاه بازیکنان کار میکند، نامشخص باشد. متوجه شدهایم که با وجود این تنوع ظاهری، تولیدکنندگان کنترلکننده در سراسر جهان تمایل دارند بهطور مداوم از سه سبک مختلف کنترلکننده پیروی کنند. برخیاز آنها کلیدهای سختافزاری برای جابهجایی بین دو یا چند مورد از این موارد دارند.
این یعنی میتوانید با حداقل سه کنترلکننده در تیم توسعهدهنده خود آزمایش کنید و مطمئن باشید که بازیتان بدون نیاز به فهرستهای مجاز و غیرمجاز قابلبازی است.
انواع رایج کنترلکننده
رایجترین سبک کنترلکنندهها معمولاً از چیدمان کنسولهای بازی محبوب تقلید میکنند. این هم ازنظر زیباییشناسی در برچسبهای دکمه و چیدمان و هم ازنظر عملکردی با رویدادهایی که ایجاد میشوند، اهمیت دارد. کنترلکنندههایی که دارای کلیدهای سختافزاری برای جابهجایی بین انواع مختلف کنسول هستند، رویدادهایی را که ارسال میکنند تغییر میدهند و اغلب حتی چیدمان دکمه منطقی آنها نیز تغییر میکند.
هنگام آزمایش، توصیه میکنیم درستیسنجی کنید که بازیتان با یک کنترلکننده در هر دسته کار میکند. میتوانید انتخاب کنید که با کنترلکنندههای طرف اول یا تولیدکنندگان طرف سوم محبوب آزمایش کنید. بهطورکلی، ما محبوبترین کنترلرها را با تمام تلاش به تعریف بالا نگاشت خواهیم کرد.
| نوع کنترلکننده | تفاوتهای رفتاری | گونههای برچسبگذاری |
|---|---|---|
| کنترلکنندههای سبک Xbox
اینها کنترلکنندههایی هستند که معمولاً برای پلاتفرم Microsoft Xbox و Windows* ساخته میشوند. |
این کنترلکنندهها با مجموعه ویژگیهای شرحدادهشده در پردازش ورودیهای کنترلکننده مطابقت دارند | دکمههای L2/R2 در این کنترلرها با برچسب LT/RT مشخص شدهاند |
| کنترلکنندههای سبک کلید
این کنترلکنندهها معمولاً برای خانواده کنسولهای Nintendo Switch* طراحی شدهاند. |
این کنترلکنندهها KeyEvent
KEYCODE_BUTTON_R2
KEYCODE_BUTTON_L2
MotionEvent را ارسال میکنند |
دکمههای L2/R2 در این کنترلکنندهها با برچسب ZL/ZR مشخص شدهاند.
این کنترلکنندهها همچنین دکمههای A و B و دکمههای
X و Y را جابهجا میکنند، بنابراین |
| کنترلکنندههای سبک PlayStation
این کنترلکنندهها معمولاً برای خانواده کنسولهای Sony PlayStation* طراحی شدهاند. |
این کنترلکنندهها MotionEvents
مثل کنترلکنندههای سبک Xbox ارسال میکنند، اما وقتی کاملاً فشرده میشوند KeyEvents
مثل کنترلکنندههای سبک Switch نیز ارسال میکنند. |
این کنترلکنندهها از مجموعه متفاوتی از گلیفها برای دکمههای چهره استفاده میکنند. |
* Microsoft، Xbox، و Windows علامتهای تجاری ثبتشده Microsoft هستند؛ Nintendo Switch علامت تجاری ثبتشده Nintendo of America Inc است؛ PlayStation علامت تجاری ثبتشده Sony Interactive Entertainment Inc. است.
دکمههای راهانداز ابهامزدایی
برخیاز کنترلکنندهها AXIS_LTRIGGER و AXIS_RTRIGGER را ارسال میکنند، برخی دیگر KEYCODE_BUTTON_L2 و KEYCODE_BUTTON_R2 را ارسال میکنند، و برخی دیگر همه این رویدادها را براساس قابلیتهای سختافزاریشان ارسال میکنند. با پشتیبانی از همه این رویدادها، سازگاری را به حداکثر برسانید.
همه کنترلکنندههایی که AXIS_LTRIGGER را ارسال میکنند، AXIS_BRAKE را نیز ارسال میکنند، بههمین ترتیب
برای AXIS_RTRIGGER و AXIS_GAS برای کمک به حداکثر رساندن سازگاری بین چرخهای مسابقه
و کنترلکنندههای بازی معمولی. بهطورکلی این کار مشکلی ایجاد نمیکند، اما برای ویژگیهایی مثل صفحههای تخصیص مجدد کلیدها مراقب باشید.
| رهانداز | MotionEvent |
KeyEvent |
|---|---|---|
| راهانداز چپ | AXIS_LTRIGGERAXIS_BRAKE
|
KEYCODE_BUTTON_L2
|
| راهانداز راست | AXIS_RTRIGGERAXIS_GAS
|
KEYCODE_BUTTON_R2
|
باید دقت شود که بازی شما بتواند هم KeyEvent و هم MotionEvent را مدیریت کند تا با بیشترین تعداد ممکن کنترلر سازگار باشد و رویدادها تکراری نباشند.
کنترلکنندههای پشتیبانیشده
هنگام آزمایش، توصیه میکنیم درستیسنجی کنید که بازیتان با یک کنترلکننده در هر دسته کار میکند.
- سبک Xbox
- سبک Nintendo Switch
- سبک PlayStation
میتوانید با کنترلکنندههای طرف اول یا تولیدکنندگان طرف سوم محبوب آزمایش کنید، و بهطورکلی کنترلکنندههای محبوب را تا حد امکان به تعریف نزدیکتر تخصیص میدهیم.