רוב המשחקים מיועדים לתמוך במשתמש אחד בכל מכשיר Android, אבל אפשר גם לתמוך בכמה משתמשים באמצעות שלטי משחקים שמחוברים בו-זמנית לאותו מכשיר Android.
בשיעור הזה נסביר על כמה טכניקות בסיסיות לטיפול בקלט במשחק רב-משתתפים במכשיר יחיד, מכמה שלטים מחוברים. הפעולות האלה כוללות שמירה של מיפוי בין הדמויות של השחקנים לבין כל שלטי המשחק, ועיבוד של אירועי קלט של השלט בהתאם.
מיפוי שחקנים למזהי מכשירים של שלטי משחק
כששלט משחק מחובר למכשיר עם Android, המערכת מקצה לו מזהה מכשיר שהוא מספר שלם. אפשר לקבל את מזהי המכשירים של שלטי משחק מחוברים על ידי קריאה ל-InputDevice.getDeviceIds(), כמו שמוצג בקטע אימות של חיבור שלט המשחק. לאחר מכן תוכלו לשייך כל מזהה מכשיר לשחקן במשחק, ולעבד את פעולות המשחק של כל שחקן בנפרד.
בקטע הקוד הבא אפשר לראות איך משתמשים ב-SparseArray כדי לשייך דמות של שחקן לשלט ספציפי. בדוגמה הזו, המשתנה mShips מאחסן אוסף של אובייקטים מסוג Ship. כשמשתמש מחבר שלט חדש, נוצרת דמות חדשה של שחקן במשחק. הדמות מוסרת כשמסירים את השלט שמשויך אליה.
השיטות להחזרת קריאה onInputDeviceAdded() ו-onInputDeviceRemoved() הן חלק משכבת ההפשטה שמפורטת במאמר תמיכה בשלטי משחק בגרסאות שונות של Android.
כשמטמיעים את פונקציות ה-callback האלה של listener, המשחק יכול לזהות את מזהה המכשיר של שלט המשחק כשמוסיפים או מסירים שלט. הזיהוי הזה פועל ב-Android מגרסה 2.3 (רמת API 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);
}