תמיכה בכמה שלטים לגיימינג

רוב המשחקים מיועדים לתמיכה במשתמש אחד בכל מכשיר מבוסס-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);
}

עיבוד של קלט מכמה בקרי משחקים

המשחק צריך להריץ את הלולאה הבאה כדי לעבד קלט מכמה בקרים:

  1. זיהוי של אירוע קלט.
  2. מזהים את מקור הקלט ואת מזהה המכשיר שלו.
  3. על סמך הפעולה שמצוינת בקוד המקש של אירוע הקלט או בערך הציר, מעדכנים את האווטאר של השחקן שמשויך למזהה המכשיר הזה.
  4. הצגה ועדכון של ממשק המשתמש.

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);
}