در سطح سیستم، Android کدهای رویداد ورودی را از کنترلکنندههای بازی به عنوان کدهای کلید اندروید و مقادیر محور گزارش میکند. در بازی خود می توانید این کدها و مقادیر را دریافت کرده و آنها را به اکشن های خاص درون بازی تبدیل کنید.
هنگامی که بازیکنان به صورت فیزیکی یک کنترلر بازی را به دستگاه های مجهز به اندروید خود متصل می کنند یا به صورت بی سیم جفت می کنند، سیستم به طور خودکار کنترلر را به عنوان دستگاه ورودی شناسایی می کند و شروع به گزارش رویدادهای ورودی آن می کند. بازی شما میتواند این رویدادهای ورودی را با پیادهسازی روشهای پاسخ به تماس زیر در Activity
فعال یا View
متمرکز خود دریافت کند (شما باید پاسخهای تماس را برای Activity
یا View
اجرا کنید، اما نه هر دو):
- از
Activity
:-
dispatchGenericMotionEvent(android.view. MotionEvent)
فراخوانی شده برای پردازش رویدادهای حرکتی عمومی مانند حرکات جوی استیک.
-
dispatchKeyEvent(android.view.KeyEvent)
برای پردازش رویدادهای کلیدی مانند فشار دادن یا رها کردن یک گیم پد یا دکمه D-pad فراخوانی می شود.
-
- از
View
:-
onGenericMotionEvent(android.view.MotionEvent)
فراخوانی شده برای پردازش رویدادهای حرکتی عمومی مانند حرکات جوی استیک.
-
onKeyDown(int, android.view.KeyEvent)
برای پردازش فشار یک کلید فیزیکی مانند صفحه بازی یا دکمه D-pad فراخوانی می شود.
-
onKeyUp(int, android.view.KeyEvent)
برای پردازش یک کلید فیزیکی مانند گیم پد یا دکمه D-pad فراخوانی می شود.
-
رویکرد توصیه شده این است که رویدادها را از یک شی View
خاص که کاربر با آن تعامل دارد، ضبط کنید. برای دریافت اطلاعات در مورد نوع رویداد ورودی دریافتی، اشیای زیر را که توسط callback ها ارائه می شود، بررسی کنید:
-
KeyEvent
- شیئی که رویدادهای پد جهت (D-pad) و دکمه گیم پد را توصیف می کند. رویدادهای کلیدی با یک کد کلیدی همراه میشوند که نشان میدهد دکمه خاصی فعال شده است، مانند
DPAD_DOWN
یاBUTTON_A
. میتوانید کد کلید را با فراخوانیgetKeyCode()
یا از تماسهای رویداد کلیدی مانندonKeyDown()
دریافت کنید. -
MotionEvent
- جسمی که ورودی جوی استیک و حرکات ماشه شانه را توصیف می کند. رویدادهای حرکت با یک کد اقدام و مجموعه ای از مقادیر محور همراه هستند. کد عمل تغییر حالتی را که مانند حرکت جوی استیک رخ داده است را مشخص می کند. مقادیر محور موقعیت و سایر ویژگیهای حرکتی را برای یک کنترل فیزیکی خاص، مانند
AXIS_X
یاAXIS_RTRIGGER
توصیف میکنند. میتوانید کد عمل را با فراخوانیgetAction()
و مقدار محور را با فراخوانیgetAxisValue()
دریافت کنید.
این درس بر این تمرکز دارد که چگونه میتوانید ورودی رایجترین کنترلهای فیزیکی (دکمههای گیمپد، پدهای جهتگیری، و جوی استیکها) را در صفحه بازی با اجرای روشهای برگشت به تماس View
بالا و پردازش اشیاء KeyEvent
و MotionEvent
مدیریت کنید.
بررسی کنید که یک کنترلر بازی متصل است
هنگام گزارش رویدادهای ورودی، Android بین رویدادهایی که از یک دستگاه کنترلکننده غیربازی آمدهاند و رویدادهایی که از یک کنترلکننده بازی آمدهاند، تمایز قائل نمیشود. به عنوان مثال، یک عمل صفحه لمسی یک رویداد AXIS_X
ایجاد می کند که مختصات X سطح لمسی را نشان می دهد، اما یک جوی استیک یک رویداد AXIS_X
ایجاد می کند که نشان دهنده موقعیت X جوی استیک است. اگر بازی شما به مدیریت ورودی کنترلر بازی اهمیت می دهد، ابتدا باید بررسی کنید که رویداد ورودی از یک نوع منبع مرتبط باشد.
برای تأیید اینکه یک دستگاه ورودی متصل یک کنترلکننده بازی است، getSources()
را فراخوانی کنید تا یک فیلد بیت ترکیبی از انواع منبع ورودی که در آن دستگاه پشتیبانی میشوند به دست آورید. سپس می توانید تست کنید تا ببینید آیا فیلدهای زیر تنظیم شده اند یا خیر:
- یک نوع منبع از
SOURCE_GAMEPAD
نشان میدهد که دستگاه ورودی دارای دکمههای صفحه بازی است (به عنوان مثال،BUTTON_A
). توجه داشته باشید که این نوع منبع دقیقاً نشان نمیدهد که آیا کنترلکننده بازی دارای دکمههای D-pad است یا خیر، اگرچه اکثر گیمپدها معمولاً دارای کنترلهای جهتدار هستند. - یک نوع منبع از
SOURCE_DPAD
نشان می دهد که دستگاه ورودی دارای دکمه های D-pad است (به عنوان مثال،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 (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD || sources and InputDevice.SOURCE_JOYSTICK == InputDevice.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); 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; }
علاوه بر این، ممکن است بخواهید قابلیتهای ورودی فردی که توسط یک کنترلر بازی متصل پشتیبانی میشود را بررسی کنید. این ممکن است مفید باشد، برای مثال، اگر میخواهید بازی شما فقط از ورودی مجموعه کنترلهای فیزیکی که میداند استفاده کند.
برای تشخیص اینکه آیا یک کد کلید یا کد محور خاص توسط یک کنترلر بازی متصل پشتیبانی می شود، از این تکنیک ها استفاده کنید:
- در اندروید 4.4 (سطح API 19) یا بالاتر، میتوانید با فراخوانی
hasKeys(int...)
تعیین کنید که آیا یک کد کلید روی یک کنترلر بازی متصل پشتیبانی میشود یا خیر. - در Android 3.1 (سطح API 12) یا بالاتر، میتوانید با فراخوانی
getMotionRanges()
تمام محورهای موجود را که روی یک کنترلر بازی متصل پشتیبانی میشوند، پیدا کنید. سپس، بر روی هر شیءInputDevice.MotionRange
بازگشتی،getAxis()
را فراخوانی کنید تا شناسه محور آن را دریافت کنید.
پردازش دکمه گیم پد
شکل 1 نشان می دهد که چگونه اندروید کدهای کلید و مقادیر محور را به کنترل های فیزیکی در اکثر کنترلرهای بازی نگاشت می کند.
فراخوان های موجود در شکل به موارد زیر اشاره دارند:
کدهای کلیدی رایج که با فشار دادن دکمه های صفحه بازی ایجاد می شوند عبارتند از BUTTON_A
، BUTTON_B
، BUTTON_SELECT
و BUTTON_START
. برخی از کنترلکنندههای بازی نیز با فشار دادن مرکز نوار متقاطع D-pad، کد کلید DPAD_CENTER
را فعال میکنند. بازی شما میتواند کد کلید را با فراخوانی getKeyCode()
یا از فراخوانهای رویداد کلیدی مانند onKeyDown()
بازرسی کند، و اگر رویدادی را نشان میدهد که مربوط به بازی شما است، آن را به عنوان یک اکشن بازی پردازش کنید. جدول 1 اقدامات بازی توصیه شده برای رایج ترین دکمه های گیم پد را فهرست می کند.
بازی اکشن | کد کلید دکمه |
---|---|
شروع بازی در منوی اصلی، یا مکث/لغو مکث در طول بازی | BUTTON_START * |
نمایش منو | BUTTON_SELECT * و KEYCODE_MENU * |
مانند رفتار ناوبری Android Back که در راهنمای طراحی ناوبری توضیح داده شده است. | KEYCODE_BACK |
به یک مورد قبلی در یک منو برگردید | BUTTON_B |
انتخاب را تأیید کنید یا اقدام اصلی بازی را انجام دهید | BUTTON_A و DPAD_CENTER |
* بازی شما نباید به وجود دکمه های Start، Select یا Menu متکی باشد.
نکته: یک صفحه پیکربندی را در بازی خود در نظر بگیرید تا به کاربران امکان دهد نگاشت های کنترلر بازی خود را برای اقدامات بازی شخصی سازی کنند.
قطعه زیر نشان میدهد که چگونه میتوانید onKeyDown()
لغو کنید تا فشارهای دکمه BUTTON_A
و DPAD_CENTER
را با یک عمل بازی مرتبط کنید.
کاتلین
class GameView(...) : View(...) { ... override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { var handled = false if (event.source and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD) { if (event.repeatCount == 0) { when (keyCode) { // Handle gamepad and D-pad button presses to navigate the ship ... else -> { keyCode.takeIf { isFireKey(it) }?.run { // Update the ship object to fire lasers ... handled = true } } } } if (handled) { return true } } return super.onKeyDown(keyCode, event) } // Here we treat Button_A and DPAD_CENTER as the primary action // keys for the game. private fun isFireKey(keyCode: Int): Boolean = keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_BUTTON_A }
جاوا
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. return keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_BUTTON_A; } }
توجه: در اندروید 4.2 (سطح API 17) و پایینتر، سیستم بهطور پیشفرض با BUTTON_A
بهعنوان کلید Android Back رفتار میکند. اگر برنامه شما از این نسخههای Android پشتیبانی میکند، مطمئن شوید که BUTTON_A
به عنوان اکشن اصلی بازی در نظر بگیرید. برای تعیین نسخه Android SDK فعلی در دستگاه، به مقدار Build.VERSION.SDK_INT
مراجعه کنید.
ورودی پد جهت را پردازش کنید
پد جهت 4 جهته (D-pad) یک کنترل فیزیکی رایج در بسیاری از کنترلرهای بازی است. Android فشارهای D-pad UP و DOWN را بهعنوان رویدادهای AXIS_HAT_Y
با گستره 1.0 (بالا) تا 1.0 (پایین) گزارش میکند و D-pad LEFT یا RIGHT بهعنوان رویدادهای AXIS_HAT_X
با دامنه از -1.0 (چپ) تا 1.0 ( درست).
برخی از کنترلرها در عوض فشارهای D-pad را با یک کد کلید گزارش می دهند. اگر بازی شما به فشارهای D-pad اهمیت می دهد، باید رویدادهای محور کلاه و کدهای کلید D-pad را همانطور که در جدول 2 توصیه می شود، به عنوان رویدادهای ورودی یکسان در نظر بگیرید.
بازی اکشن | کد کلید D-pad | کد محور کلاه |
---|---|---|
حرکت به بالا | KEYCODE_DPAD_UP | AXIS_HAT_Y (برای مقادیر 0 تا -1.0) |
حرکت به پایین | KEYCODE_DPAD_DOWN | AXIS_HAT_Y (برای مقادیر 0 تا 1.0) |
حرکت به چپ | KEYCODE_DPAD_LEFT | AXIS_HAT_X (برای مقادیر 0 تا -1.0) |
حرکت به راست | KEYCODE_DPAD_RIGHT | AXIS_HAT_X (برای مقادیر 0 تا 1.0) |
قطعه کد زیر یک کلاس کمکی را نشان می دهد که به شما امکان می دهد محور کلاه و مقادیر کد کلید را از یک رویداد ورودی بررسی کنید تا جهت D-pad را تعیین کنید.
کاتلین
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. event.source and InputDevice.SOURCE_DPAD != 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. if ((event.getSource() & InputDevice.SOURCE_DPAD) != InputDevice.SOURCE_DPAD) { return true; } else { return false; } } }
شما می توانید از این کلاس کمکی در بازی خود در هر جایی که می خواهید ورودی D-pad را پردازش کنید (به عنوان مثال، در تماس های 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
مراجعه کنید.
میتوانید از اطلاعات تاریخی برای نمایش دقیقتر حرکت یک شی بازی بر اساس ورودی جوی استیک استفاده کنید. برای بازیابی مقادیر فعلی و تاریخی، 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); } }
قبل از استفاده از ورودی جوی استیک، باید مشخص کنید که جوی استیک در مرکز قرار دارد یا خیر، سپس حرکات محور آن را بر این اساس محاسبه کنید. جوی استیک ها معمولاً یک ناحیه مسطح دارند، یعنی محدوده ای از مقادیر نزدیک به مختصات (0,0) که محور در مرکز آن در نظر گرفته می شود. اگر مقدار محور گزارش شده توسط 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
است. رویدادهای محور تنها زمانی رخ میدهند که تریگر محدودهای از مقادیر بین 0 و 1 را منتشر کند و برخی از کنترلکنندهها با خروجی آنالوگ علاوه بر رویدادهای محور، رویدادهای دکمهای را نیز منتشر میکنند. بازیها باید از هر دو رویدادAXIS_
وKEYCODE_BUTTON_
پشتیبانی کنند تا با همه کنترلکنندههای بازی رایج سازگار باشند، اما اگر کنترلکننده هر دو را گزارش کند، رویدادی را ترجیح میدهند که بیشترین حس را برای گیمپلی شما داشته باشد. در Android 4.3 (سطح API 18) و بالاتر، کنترلکنندهای کهAXIS_LTRIGGER
تولید میکند نیز مقدار یکسانی را برای محورAXIS_BRAKE
گزارش میکند. همین امر برایAXIS_RTRIGGER
وAXIS_GAS
نیز صادق است. اندروید تمام فشارهای ماشه آنالوگ را با مقدار نرمال شده از 0.0 (آزاد شده) تا 1.0 (فشار کامل) گزارش می دهد. - رفتارها و پشتیبانی خاص ممکن است در محیط های شبیه سازی شده متفاوت باشد . پلتفرمهای شبیهسازیشده، مانند بازیهای Google Play ، ممکن است بر اساس قابلیتهای سیستم عامل میزبان، از نظر رفتار کمی متفاوت باشند. برای مثال، برخی از کنترلکنندههایی که رویدادهای
AXIS_
وKEYCODE_BUTTON_
را منتشر میکنند، فقط رویدادهایAXIS_
را منتشر میکنند، و پشتیبانی از برخی کنترلکنندهها ممکن است به طور کامل وجود نداشته باشد.