إتاحة استخدام عدة أذرع تحكُّم في الألعاب
تنظيم صفحاتك في مجموعات
يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.
على الرغم من أنّ معظم الألعاب مصمّمة لدعم مستخدم واحد لكل جهاز Android،
يمكن أيضًا دعم مستخدمين متعددين باستخدام وحدات تحكم في الألعاب
متصلين في نفس الوقت على نفس جهاز Android.
يتناول هذا الدرس بعض الأساليب الأساسية للتعامل مع الإدخال في
هي لعبة متعددة اللاعبين على جهاز من وحدات تحكّم متعدّدة متصلة. وتشمل هذه المعلومات ما يلي:
الحفاظ على الربط بين الصور الرمزية للاعب وكل جهاز من أجهزة التحكم
معالجة أحداث وحدة التحكم التي يتم إدخالها بشكل مناسب.
ربط اللاعبين بأرقام تعريف أجهزة وحدات التحكّم
عند توصيل ذراع تحكّم في الألعاب بجهاز Android، يطبّق النظام
يعيّن له رقم تعريف جهاز عددًا صحيحًا. يمكنك الحصول على أرقام تعريف الأجهزة المتصلة
وحدات التحكّم في الألعاب من خلال طلب الرقم InputDevice.getDeviceIds()
، على النحو الموضّح في التحقّق من أنّ ذراع التحكّم في الألعاب متصل. يمكنك بعد ذلك ربط كل
رقم تعريف الجهاز للاعب في لعبتك، ومعالجة إجراءات اللعبة لكل لاعب على حدة.
ملاحظة: على الأجهزة التي تعمل بنظام التشغيل Android 4.1 (واجهة برمجة التطبيقات)
المستوى 16 وأعلى، يمكنك الحصول على واصف جهاز الإدخال باستخدام
getDescriptor()
، الذي يُرجع قيمة فريدة
قيمة سلسلة ثابتة لجهاز الإدخال. وعلى عكس رقم تعريف الجهاز، فإن الواصف
لن تتغيّر حتى إذا كان جهاز الإدخال غير متصل أو تمت إعادة توصيله أو
تمت إعادة الضبط.
يوضح مقتطف الرمز أدناه كيفية استخدام SparseArray
لربط الصورة الرمزية للّاعب بوحدة تحكّم معيّنة. في هذا المثال، تشير
يخزِّن المتغيّر mShips
مجموعة من عناصر Ship
. تحوّل في
يتم إنشاء الصورة الرمزية للاعب داخل اللعبة عند إرفاق وحدة تحكم جديدة من قبل المستخدم،
وتتم إزالته عند إزالة وحدة التحكم المرتبطة به.
معاودة الاتصال "onInputDeviceAdded()
" و"onInputDeviceRemoved()
"
تعتبر الطرق جزءًا من طبقة التجريد التي تم تقديمها في
توفير وحدات التحكّم في إصدارات Android من خلال تنفيذ هذه
يمكن للّعبة التعرف على رقم تعريف جهاز وحدة التحكم في الألعاب عند
إضافة أو إزالة وحدة التحكم. يتوافق هذا الاكتشاف مع Android 2.3
(المستوى 9 من واجهة برمجة التطبيقات) والإصدارات الأحدث
Kotlin
private val ships = SparseArray<Ship>()
override fun onInputDeviceAdded(deviceId: Int) {
getShipForID(deviceId)
}
override fun onInputDeviceRemoved(deviceId: Int) {
removeShipForID(deviceId)
}
private fun getShipForID(shipID: Int): Ship {
return ships.get(shipID) ?: Ship().also {
ships.append(shipID, it)
}
}
private fun removeShipForID(shipID: Int) {
ships.remove(shipID)
}
Java
private final SparseArray<Ship> ships = new SparseArray<Ship>();
@Override
public void onInputDeviceAdded(int deviceId) {
getShipForID(deviceId);
}
@Override
public void onInputDeviceRemoved(int deviceId) {
removeShipForID(deviceId);
}
private Ship getShipForID(int shipID) {
Ship currentShip = ships.get(shipID);
if ( null == currentShip ) {
currentShip = new Ship();
ships.append(shipID, currentShip);
}
return currentShip;
}
private void removeShipForID(int shipID) {
ships.remove(shipID);
}
معالجة إدخالات وحدة تحكم متعددة
يجب أن تنفّذ لعبتك التكرار الحلقي التالي لمعالجتها
الإدخال من وحدات تحكُّم متعددة:
- اكتشاف ما إذا كان حدث إدخال قد وقع أم لا
- حدِّد مصدر الإدخال ورقم تعريف الجهاز الخاص به.
- بالاستناد إلى الإجراء المشار إليه في رمز مفتاح حدث الإدخال أو قيمة المحور،
تحديث الصورة الرمزية للّاعب المرتبطة برقم تعريف الجهاز هذا.
- عرض واجهة المستخدم وتعديلها
إدخال KeyEvent
وMotionEvent
للأحداث أرقام تعريف أجهزة مرتبطة بها. تستفيد لعبتك من
لتحديد وحدة التحكّم التي صدر منها حدث الإدخال، وتعديل
الصورة الرمزية للّاعب المرتبطة بوحدة التحكم هذه.
يعرض مقتطف الرمز التالي كيفية الحصول على مرجع الصورة الرمزية للاعب.
يتوافق مع رقم تعريف جهاز التحكم في الألعاب، وتحديث اللعبة استنادًا إلى
المستخدم في وحدة التحكم هذه.
Kotlin
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (event.source and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD) {
event.deviceId.takeIf { it != -1 }?.also { deviceId ->
val currentShip: Ship = getShipForID(deviceId)
// Based on which key was pressed, update the player avatar
// (e.g. set the ship headings or fire lasers)
return true
}
}
return super.onKeyDown(keyCode, event)
}
Java
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)
== InputDevice.SOURCE_GAMEPAD) {
int deviceId = event.getDeviceId();
if (deviceId != -1) {
Ship currentShip = getShipForId(deviceId);
// Based on which key was pressed, update the player avatar
// (e.g. set the ship headings or fire lasers)
...
return true;
}
}
return super.onKeyDown(keyCode, event);
}
ملاحظة: كإحدى أفضل الممارسات، عندما ينقر المستخدم
قطع اتصال ذراع التحكّم في الألعاب، عليك إيقاف اللعبة مؤقتًا والاستفسار عمّا إذا كان المستخدم
تريد إعادة الاتصال.
يخضع كل من المحتوى وعيّنات التعليمات البرمجية في هذه الصفحة للتراخيص الموضحّة في ترخيص استخدام المحتوى. إنّ Java وOpenJDK هما علامتان تجاريتان مسجَّلتان لشركة Oracle و/أو الشركات التابعة لها.
تاريخ التعديل الأخير: 2025-07-26 (حسب التوقيت العالمي المتفَّق عليه)
[[["يسهُل فهم المحتوى.","easyToUnderstand","thumb-up"],["ساعَدني المحتوى في حلّ مشكلتي.","solvedMyProblem","thumb-up"],["غير ذلك","otherUp","thumb-up"]],[["لا يحتوي على المعلومات التي أحتاج إليها.","missingTheInformationINeed","thumb-down"],["الخطوات معقدة للغاية / كثيرة جدًا.","tooComplicatedTooManySteps","thumb-down"],["المحتوى قديم.","outOfDate","thumb-down"],["ثمة مشكلة في الترجمة.","translationIssue","thumb-down"],["مشكلة في العيّنات / التعليمات البرمجية","samplesCodeIssue","thumb-down"],["غير ذلك","otherDown","thumb-down"]],["تاريخ التعديل الأخير: 2025-07-26 (حسب التوقيت العالمي المتفَّق عليه)"],[],[],null,["# Support multiple game controllers\n\nWhile most games are designed to support a single user per Android device,\nit's also possible to support multiple users with game controllers that are\nconnected simultaneously on the same Android device.\n\nThis lesson covers some basic techniques for handling input in your single\ndevice multiplayer game from multiple connected controllers. This includes\nmaintaining a mapping between player avatars and each controller device and\nprocessing controller input events appropriately.\n\nMap players to controller device IDs\n------------------------------------\n\nWhen a game controller is connected to an Android device, the system\nassigns it an integer device ID. You can obtain the device IDs for connected\ngame controllers by calling [InputDevice.getDeviceIds()](/reference/android/view/InputDevice#getDeviceIds()), as shown in [Verify a Game Controller is Connected](/develop/ui/views/touch-and-input/game-controllers/controller-input#input). You can then associate each\ndevice ID with a player in your game, and process game actions for each player separately.\n\n**Note:** On devices running Android 4.1 (API\nlevel 16) and higher, you can obtain an input device's descriptor using\n[getDescriptor()](/reference/android/view/InputDevice#getDescriptor()), which returns a unique\npersistent string value for the input device. Unlike a device ID, the descriptor\nvalue won't change even if the input device is disconnected, reconnected, or\nreconfigured.\n\nThe code snippet below shows how to use a [SparseArray](/reference/android/util/SparseArray)\nto associate a player's avatar with a specific controller. In this example, the\n`mShips` variable stores a collection of `Ship` objects. A new\nplayer avatar is created in-game when a new controller is attached by a user,\nand removed when its associated controller is removed.\n\nThe `onInputDeviceAdded()` and `onInputDeviceRemoved()` callback\nmethods are part of the abstraction layer introduced in\n[Supporting Controllers Across Android Versions](/training/game-controllers/compatibility#status_callbacks}). By implementing these\nlistener callbacks, your game can identify the game controller's device ID when a\ncontroller is added or removed. This detection is compatible with Android 2.3\n(API level 9) and higher. \n\n### Kotlin\n\n```kotlin\nprivate val ships = SparseArray\u003cShip\u003e()\n\noverride fun onInputDeviceAdded(deviceId: Int) {\n getShipForID(deviceId)\n}\n\noverride fun onInputDeviceRemoved(deviceId: Int) {\n removeShipForID(deviceId)\n}\n\nprivate fun getShipForID(shipID: Int): Ship {\n return ships.get(shipID) ?: Ship().also {\n ships.append(shipID, it)\n }\n}\n\nprivate fun removeShipForID(shipID: Int) {\n ships.remove(shipID)\n}\n```\n\n### Java\n\n```java\nprivate final SparseArray\u003cShip\u003e ships = new SparseArray\u003cShip\u003e();\n\n@Override\npublic void onInputDeviceAdded(int deviceId) {\n getShipForID(deviceId);\n}\n\n@Override\npublic void onInputDeviceRemoved(int deviceId) {\n removeShipForID(deviceId);\n}\n\nprivate Ship getShipForID(int shipID) {\n Ship currentShip = ships.get(shipID);\n if ( null == currentShip ) {\n currentShip = new Ship();\n ships.append(shipID, currentShip);\n }\n return currentShip;\n}\n\nprivate void removeShipForID(int shipID) {\n ships.remove(shipID);\n}\n```\n\nProcess multiple controller input\n---------------------------------\n\nYour game should execute the following loop to process\ninput from multiple controllers:\n\n1. Detect whether an input event occurred.\n2. Identify the input source and its device ID.\n3. Based on the action indicated by the input event key code or axis value, update the player avatar associated with that device ID.\n4. Render and update the user interface.\n\n[KeyEvent](/reference/android/view/KeyEvent) and [MotionEvent](/reference/android/view/MotionEvent) input\nevents have device IDs associated with them. Your game can take advantage of\nthis to determine which controller the input event came from, and update the\nplayer avatar associated with that controller.\n\nThe following code snippet shows how you might get a player avatar reference\ncorresponding to a game controller device ID, and update the game based on the\nuser's button press on that controller. \n\n### Kotlin\n\n```kotlin\noverride fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {\n if (event.source and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD) {\n event.deviceId.takeIf { it != -1 }?.also { deviceId -\u003e\n val currentShip: Ship = getShipForID(deviceId)\n // Based on which key was pressed, update the player avatar\n // (e.g. set the ship headings or fire lasers)\n return true\n }\n }\n return super.onKeyDown(keyCode, event)\n}\n```\n\n### Java\n\n```java\n@Override\npublic boolean onKeyDown(int keyCode, KeyEvent event) {\n if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)\n == InputDevice.SOURCE_GAMEPAD) {\n int deviceId = event.getDeviceId();\n if (deviceId != -1) {\n Ship currentShip = getShipForId(deviceId);\n // Based on which key was pressed, update the player avatar\n // (e.g. set the ship headings or fire lasers)\n ...\n return true;\n }\n }\n return super.onKeyDown(keyCode, event);\n}\n```\n\n**Note:**As a best practice, when a user's\ngame controller disconnects, you should pause the game and ask if the user\nwants to reconnect."]]