على الرغم من أنّ معظم الألعاب مصمّمة لتتيح استخدام حساب واحد لكل جهاز Android، يمكن أيضًا إتاحة استخدام حسابات متعددة مع وحدات تحكّم في الألعاب متصلة في الوقت نفسه بالجهاز نفسه الذي يعمل بنظام التشغيل Android.
يتناول هذا الدرس بعض التقنيات الأساسية للتعامل مع الإدخال في لعبة متعددة اللاعبين على جهاز واحد باستخدام وحدات تحكّم متعددة متصلة. ويشمل ذلك الحفاظ على ربط بين الصور الرمزية للاعبين وكل جهاز تحكّم ومعالجة أحداث إدخال وحدة التحكّم بشكل مناسب.
ربط اللاعبين بأرقام تعريف أجهزة التحكّم
عند توصيل وحدة تحكّم بالألعاب بجهاز يعمل بنظام التشغيل Android، يمنحها النظام رقم تعريف للجهاز عبارة عن عدد صحيح. يمكنك الحصول على أرقام تعريف وحدات التحكّم في الألعاب المتصلة من خلال استدعاء InputDevice.getDeviceIds()، كما هو موضّح في التأكّد من اتصال وحدة تحكّم في الألعاب. يمكنك بعد ذلك ربط كل معرّف جهاز بلاعب في لعبتك، ومعالجة إجراءات اللعبة لكل لاعب على حدة.
يوضّح مقتطف الرمز البرمجي كيفية استخدام 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);
}