على مستوى النظام، يبلغ Android عن رموز أحداث الإدخال من وحدات تحكّم الألعاب على أنّها رموز مفاتيح Android وقيم المحاور. في لعبتك، يمكنك استلام هذه الرموز والقيم وتحويلها إلى إجراءات محددة داخل اللعبة.
عندما يتصل اللاعبون فعليًا بوحدة تحكم في الألعاب أو يقرنونها لاسلكيًا بأجهزتهم التي تعمل بنظام التشغيل Android، يكتشف النظام تلقائيًا وحدة التحكم
كجهاز إدخال ويبدأ في الإبلاغ عن أحداث الإدخال. يمكن أن تتلقّى لعبتك
أحداث الإدخال هذه من خلال تنفيذ طرق معاودة الاتصال التالية في
Activity
النشطة أو View
المركزة (يجب
تنفيذ عمليات معاودة الاتصال إما مع Activity
أو
View
، ولكن ليس لكليهما):
- من
Activity
:dispatchGenericMotionEvent(android.view. MotionEvent)
تتم استدعاء هذه الطريقة لمعالجة أحداث الحركة العامة، مثل حركات ذراع التحكّم.
dispatchKeyEvent(android.view.KeyEvent)
يتم استدعاء هذه الدالة لمعالجة أحداث رئيسية، مثل الصحافة أو تحرير لوحة الألعاب أو زر لوحة التحكّم.
- من
View
:onGenericMotionEvent(android.view.MotionEvent)
تتم استدعاء هذه الطريقة لمعالجة أحداث الحركة العامة، مثل حركات ذراع التحكّم.
onKeyDown(int, android.view.KeyEvent)
يُقصد به معالجة الضغط على مفتاح مادي مثل لوحة الألعاب أو زرّ لوحة التحكّم.
onKeyUp(int, android.view.KeyEvent)
يُقصد به معالجة إصدار مفتاح مادي مثل لوحة الألعاب أو زرّ لوحة التحكّم.
والنهج المقترَح هو التقاط الأحداث من
عنصر View
المحدّد الذي يتفاعل معه المستخدم.
افحص العناصر التالية التي توفّرها عمليات معاودة الاتصال للحصول على معلومات عن نوع حدث الإدخال الذي تم تلقّيه:
KeyEvent
- يشير إلى عنصر يصف أحداث
لوحة الاتجاهات (لوحة التحكّم) وزر لوحة التحكّم في الألعاب. تكون الأحداث الرئيسية مصحوبة
برمز مفتاح يشير إلى الزر المحدّد الذي تم تشغيله، مثل
DPAD_DOWN
أوBUTTON_A
. يمكنك الحصول على رمز المفتاح من خلال استدعاء الرقمgetKeyCode()
أو من عمليات استدعاء الحدث الرئيسي مثلonKeyDown()
. MotionEvent
- كائن يصف الإدخال من ذراع التحكّم وحركات الكتف. تكون أحداث الحركة مصحوبةً برمز إجراء ومجموعة من
قيم المحاور. يحدد رمز الإجراء تغيير الحالة الذي حدث
مثل تحريك ذراع التحكم. وتصف قيم المحور الموضع وخصائص الحركة الأخرى لعنصر تحكّم مادي محدّد، مثل
AXIS_X
أوAXIS_RTRIGGER
. يمكنك الحصول على رمز الإجراء عن طريق استدعاءgetAction()
وقيمة المحور عن طريق استدعاءgetAxisValue()
.
يركز هذا الدرس على كيفية التعامل مع الإدخال من أنواع عناصر التحكم المادية الأكثر شيوعًا (أزرار لوحة الألعاب ولوحات الاتجاهات وأذرع التحكّم) في شاشة اللعبة من خلال تنفيذ
أساليب استدعاء View
المذكورة أعلاه والمعالجة
وكائنات KeyEvent
وMotionEvent
المذكورة أعلاه.
التأكّد من ربط وحدة تحكّم في الألعاب
عند الإبلاغ عن أحداث الإدخال، لا يميّز Android
بين الأحداث الواردة من جهاز غير وحدة تحكُّم بالألعاب والأحداث التي تأتي
من وحدة تحكُّم الألعاب. على سبيل المثال، يؤدي إجراء على الشاشة التي تعمل باللمس إلى إنشاء
حدث AXIS_X
الذي يمثّل الإحداثي "س"
على سطح اللمس، في حين ينشئ ذراع التحكّم الحدث
AXIS_X
الذي يمثّل الموضع "س" لذراع التحكّم. إذا كانت لعبتك تهتم بالتعامل مع إدخالات وحدة التحكم في الألعاب، يجب أن تتحقق أولاً من أن مصدر حدث الإدخال يأتي من نوع مصدر ذي صلة.
للتحقّق من أنّ جهاز الإدخال المتصل هو وحدة تحكّم في الألعاب، عليك استدعاء
getSources()
للحصول على حقل بت مدمج
لأنواع مصادر الإدخال المتوافقة مع هذا الجهاز. يمكنك بعد ذلك الاختبار لمعرفة ما إذا كانت
الحقول التالية قد تم تعيينها:
- يشير نوع المصدر
SOURCE_GAMEPAD
إلى أن جهاز الإدخال يحتوي على أزرار في لوحة الألعاب (على سبيل المثال،BUTTON_A
). يُرجى العِلم أنّ هذا النوع من المصدر لا يشير بشكل صارم إلى ما إذا كانت وحدة التحكّم في الألعاب تحتوي على أزرار لوحة التحكّم، على الرغم من أنّ معظم لوحات الألعاب تتضمّن عادةً عناصر تحكّم توجيهية. - يشير نوع المصدر
SOURCE_DPAD
إلى أنّ جهاز الإدخال يتضمّن أزرارًا للوحة التحكّم (مثلاًDPAD_UP
). - يشير نوع المصدر من النوع
SOURCE_JOYSTICK
إلى أنّ جهاز الإدخال يحتوي على عصي تحكّم تناظرية (على سبيل المثال، ذراع تحكّم يسجّل الحركات على طولAXIS_X
وAXIS_Y
).
يعرض مقتطف الرمز التالي طريقة مساعِدة تتيح لك التحقّق مما إذا كانت أجهزة الإدخال المتصلة هي وحدات تحكّم في الألعاب. في هذه الحالة، تسترد الطريقة أرقام تعريف الأجهزة لوحدات التحكّم في الألعاب. يمكنك بعد ذلك ربط كل رقم تعريف جهاز بلاعب في لعبتك ومعالجة إجراءات اللعبة لكل لاعب مرتبط على حدة. لمعرفة مزيد من المعلومات حول إتاحة استخدام عدة وحدات تحكم في الألعاب ومتصلة في الوقت نفسه على جهاز Android نفسه، يمكنك الاطّلاع على مقالة إتاحة استخدام عدة وحدات تحكّم في الألعاب.
Kotlin
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 }
Java
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 من نظام التشغيل Android (المستوى 19 لواجهة برمجة التطبيقات) أو الإصدارات الأحدث، يمكنك طلب ما إذا كان رمز المفتاح متوافقًا
مع وحدة تحكّم الألعاب المرتبطة من خلال طلب التحديث
hasKeys(int...)
. - في الإصدار Android 3.1 (المستوى 12 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث، يمكنك العثور على جميع المحاور المتاحة
المتوافقة مع وحدة تحكّم الألعاب المتصلة من خلال طلب
getMotionRanges()
أولاً. بعد ذلك، استدعِgetAxis()
في كل عنصرInputDevice.MotionRange
تم عرضه، للحصول على رقم تعريف المحور.
معالجة الضغط على زر لوحة الألعاب
يوضح الشكل 1 كيفية تعيين Android للرموز الرئيسية وقيم المحاور لعناصر التحكم الفعلية في معظم وحدات التحكم في الألعاب.
![](https://developer.android.com/static/images/training/game-controller-profiles.png?authuser=5&hl=ar)
الشكل 1. ملف شخصي لوحدة تحكم عامة في الألعاب
تشير وسائل الشرح في الشكل إلى ما يلي:
تشتمل رموز المفاتيح الشائعة التي يتم إنشاؤها من خلال الضغط على أزرار لوحة الألعاب على BUTTON_A
وBUTTON_B
وBUTTON_SELECT
وBUTTON_START
. وتشغّل بعض
وحدات التحكّم في الألعاب أيضًا رمز المفتاح DPAD_CENTER
عند الضغط على منتصف شريط لوحة التحكّم. يمكن
للعبتك فحص رمز المفتاح من خلال طلب الرقم getKeyCode()
أو من خلال طلبات استدعاء الأحداث الرئيسية مثل
onKeyDown()
،
وإذا كان هذا الحدث يمثِّل حدثًا ذا صلة بلعبتك، يمكنك معالجته
كإجراء لعبة. ويسرد الجدول 1 إجراءات الألعاب المقترحة لأزرار لوحة الألعاب الأكثر شيوعًا.
الجدول 1. إجراءات الألعاب المقترحة لأزرار لوحة الألعاب.
ألعاب الحركة | رمز مفتاح الزر |
---|---|
يمكنك بدء اللعبة من القائمة الرئيسية أو إيقافها مؤقتًا أو استئنافها أثناء اللعب. | BUTTON_START * |
عرض القائمة | BUTTON_SELECT *
وKEYCODE_MENU * |
مثل سلوك التنقّل للرجوع في نظام التشغيل Android الموضح في دليل تصميم التنقل. | KEYCODE_BACK |
الرجوع إلى عنصر سابق في قائمة | BUTTON_B |
تأكيد اختيارك أو تنفيذ إجراء أساسي في اللعبة | BUTTON_A و
DPAD_CENTER |
* يجب ألا تعتمد لعبتك على أزرار "البدء" أو "اختيار" أو القائمة.
نصيحة: يمكنك توفير شاشة إعداد في لعبتك للسماح للمستخدمين بتخصيص تعيينات وحدات التحكم في الألعاب من أجل تنفيذ إجراءات اللعبة.
يعرض المقتطف التالي كيفية إلغاء
onKeyDown()
لربط
ضغطة الزرّ BUTTON_A
وDPAD_CENTER
بإجراء لعبة ما.
Kotlin
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 }
Java
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; } }
ملاحظة: في نظام التشغيل Android 4.2 (المستوى 17
من واجهة برمجة التطبيقات) والإصدارات الأقدم، يتعامل النظام مع
BUTTON_A
كمفتاح
رجوع في Android بشكل تلقائي. إذا كان تطبيقك متوافقًا مع هذه الإصدارات
من Android، احرص على التعامل مع
BUTTON_A
باعتباره الإجراء
الأساسي في اللعبة. لتحديد الإصدار الحالي لحزمة تطوير البرامج (SDK) لنظام التشغيل Android على الجهاز،
يمكنك الرجوع إلى القيمة Build.VERSION.SDK_INT
.
إدخال لوحة الاتجاهات الخاصة بالمعالجة
تُعتبر لوحة الاتجاهات الرباعية وسيلة التحكّم الفعلية الشائعة في العديد من وحدات التحكّم في الألعاب. يعرض نظام Android تقارير D-pad لأعلى ولأسفل كأحداث
AXIS_HAT_Y
بنطاق يتراوح
من -1.0 (لأعلى) إلى 1.0 (لأسفل)، بينما يضغط زر لوحة التحكّم LEFT أو RIGHT على
AXIS_HAT_X
أحداث بنطاق يتراوح من -1.0
(لليسار) إلى 1.0 (لليمين).
وبدلاً من ذلك، تبلّغ بعض وحدات التحكّم عن ضغطات لوحة التحكّم باستخدام رمز مفتاح. إذا كانت لعبتك تهتم بضغط لوحة التحكم، فينبغي أن تتعامل مع أحداث محور القبعة ورموز مفاتيح لوحة التحكم على أنها أحداث الإدخال نفسها، على النحو الموصى به في الجدول 2.
الجدول 2. إجراءات الألعاب التلقائية المقترَحة لرموز لوحة التحكّم وقيم محور القبعات
ألعاب الحركة | رمز مفتاح لوحة التحكّم | رمز محور Hat |
---|---|---|
نقل للأعلى | 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) |
يعرض مقتطف الرمز التالي فئة مساعِدة تتيح لك التحقّق من محور القبعة وقيم رمز المفتاح من حدث إدخال لتحديد اتجاه لوحة التحكّم.
Kotlin
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 } }
Java
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; } } }
يمكنك استخدام هذا الصف المساعِد في لعبتك في أي مكان تريد فيه معالجة
إدخال لوحة التحكّم (على سبيل المثال، في
onGenericMotionEvent()
أو
onKeyDown()
طلب معاودة الاتصال).
مثلاً:
Kotlin
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. ... }
Java
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()
لمعالجة إدخال ذراع التحكّم. يجب عليك أولاً معالجة القيم التاريخية للمحور، ثم معالجة موضعه الحالي.
Kotlin
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) } } }
Java
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()
الموضّحة أدناه.
Kotlin
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 }
Java
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; }
وبوضع كل ذلك معًا، إليك كيفية معالجة حركات ذراع التحكم في اللعبة:
Kotlin
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 }
Java
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
. تأكد من التعامل مع كلا عصي وحدة التحكم في التعليمات البرمجية الخاصة بك. - التعامل مع الضغطات على مشغّل الكتف (مع توفير طرق إدخال بديلة) تحتوي بعض وحدات التحكم على
مشغلات الكتف الأيمن والأيسر. في حال توفّر هذه المشغِّلات، يُبلغ Android عن الضغط على المشغِّل الأيسر كحدث
AXIS_LTRIGGER
وضغط المشغِّل الأيمن كحدثAXIS_RTRIGGER
. في نظام التشغيل Android 4.3 (المستوى 18 من واجهة برمجة التطبيقات)، تُبلِغ أيضًا وحدة التحكّم التي تُنتجAXIS_LTRIGGER
عن قيمة متطابقة للمحورAXIS_BRAKE
. وينطبق الأمر نفسه علىAXIS_RTRIGGER
وAXIS_GAS
. يبلغ نظام Android عن جميع ضغطات المشغل التناظري بقيمة تمت تسويتها من 0.0 (تم إصدارها) إلى 1.0 (تم الضغط عليها بالكامل). لا تحتوي جميع وحدات التحكم على مشغلات، لذا ننصحك بالسماح للّاعبين بتنفيذ هذه الإجراءات باستخدام أزرار أخرى.