على الرغم من أنّ معظم الألعاب مصمَّمة لتتيح استخدام حساب واحد لكل جهاز 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);
}