कंट्रोलर से जुड़ी कार्रवाइयां मैनेज करना

कंट्रोलर में दो तरह की कार्रवाइयां होती हैं:

  • KeyEvent का इस्तेमाल, "चालू" और "बंद" की बाइनरी स्थिति वाले किसी भी बटन के लिए किया जाता है
  • MotionEvent का इस्तेमाल, किसी भी ऐसे ऐक्सिस के लिए किया जाता है जो वैल्यू की रेंज दिखाता है. जैसे, ऐनालॉग स्टिक के लिए -1 से 1 या ऐनालॉग ट्रिगर के लिए 0 से 1.

इन इनपुट को, View से पढ़ा जा सकता है, जिसमें focus होता है.

Kotlin

override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
  if (event.isFromSource(SOURCE_GAMEPAD)
      && event.repeatCount == 0
  ) {
      Log.d("GameView", "Gamepad key pressed: $keyCode")
      return true
  }

  return super.onKeyDown(keyCode, event)
}

override fun onGenericMotionEvent(event: MotionEvent): Boolean {
  if (event.isFromSource(SOURCE_JOYSTICK)) {
      Log.d("GameView", "Gamepad event: $event")
      return true
  }

  return super.onGenericMotionEvent(event)
}

Java

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
  if (event.isFromSource(SOURCE_GAMEPAD)
          && event.getRepeatCount() == 0
  ) {
      Log.d("GameView", "Gamepad key pressed: " + keyCode);
      return true;
  }

  return super.onKeyDown(keyCode, event);
}

@Override
public boolean onGenericMotionEvent(MotionEvent event) {
  if (event.isFromSource(SOURCE_JOYSTICK)) {
      Log.d("GameView", "Gamepad event: " + event);
      return true;
  }
  return super.onGenericMotionEvent(event);
}

ज़रूरत पड़ने पर, इवेंट को सीधे Activity से भी पढ़ा जा सकता है.

पुष्टि करना कि गेम कंट्रोलर कनेक्ट है या नहीं

इनपुट इवेंट की रिपोर्ट करते समय, Android अलग-अलग इनपुट डिवाइस टाइप के लिए, एक ही कुंजी या ऐक्सिस आईडी का फिर से इस्तेमाल करेगा. उदाहरण के लिए, टचस्क्रीन पर की गई किसी कार्रवाई से AXIS_X इवेंट जनरेट होता है. यह इवेंट, टच वाली जगह के X कोऑर्डिनेट को दिखाता है. वहीं, गेमपैड से AXIS_X इवेंट जनरेट होता है. यह इवेंट, लेफ़्ट स्टिक की X पोज़िशन को दिखाता है. इसका मतलब है कि इनपुट इवेंट को सही तरीके से समझने के लिए, आपको सोर्स टाइप की जांच करनी होगी.

कनेक्ट किए गए InputDevice की पुष्टि करने के लिए कि वह गेम कंट्रोलर है या नहीं, supportsSource(int) फ़ंक्शन का इस्तेमाल करें:

  • SOURCE_GAMEPAD का सोर्स टाइप यह बताता है कि इनपुट डिवाइस में कंट्रोलर बटन हैं. जैसे,KEYCODE_BUTTON_A. ध्यान दें कि इस सोर्स टाइप से यह साफ़ तौर पर पता नहीं चलता कि गेम कंट्रोलर में डी-पैड बटन हैं या नहीं. हालांकि, ज़्यादातर कंट्रोलर में आम तौर पर डायरेक्शनल कंट्रोल होते हैं.
  • `SOURCE_DPAD` का सोर्स टाइप यह बताता है कि इनपुट डिवाइस में डी-पैड बटन हैं. जैसे, `DPAD_UP`.SOURCE_DPADDPAD_UP
  • SOURCE_JOYSTICK का सोर्स टाइप यह बताता है कि इनपुट डिवाइस में ऐनालॉग कंट्रोल स्टिक हैं. जैसे, एक जॉयस्टिक जो AXIS_X और AXIS_Y के साथ-साथ होने वाली गतिविधियों को रिकॉर्ड करती है.

यहां दिए गए कोड स्निपेट में, एक हेल्पर तरीका दिखाया गया है. इसकी मदद से, यह जांच की जा सकती है कि कनेक्ट किए गए इनपुट डिवाइस, गेम कंट्रोलर हैं या नहीं. अगर ऐसा है, तो यह तरीका गेम कंट्रोलर के डिवाइस आईडी को वापस लाता है. इसके बाद, हर डिवाइस आईडी को अपने गेम में किसी खिलाड़ी से जोड़ा जा सकता है. साथ ही, कनेक्ट किए गए हर खिलाड़ी के लिए, गेम की कार्रवाइयों को अलग-अलग प्रोसेस किया जा सकता है. एक ही Android डिवाइस पर एक साथ कनेक्ट किए गए कई गेम कंट्रोलर के बारे में ज़्यादा जानने के लिए, देखें कई गेम कंट्रोलर के लिए सहायता.

Kotlin

fun getGameControllerIds(): List<Int> {
  val gameControllerDeviceIds = mutableListOf<Int>()
  val deviceIds = InputDevice.getDeviceIds()
  deviceIds.forEach { deviceId ->
      InputDevice.getDevice(deviceId)?.apply {

          // Verify that the device has gamepad buttons, control sticks, or both.
          if (supportsSource(SOURCE_GAMEPAD)
              || supportsSource(SOURCE_JOYSTICK)) {
              // This device is a game controller. Store its device ID.
              gameControllerDeviceIds
                  .takeIf { !it.contains(deviceId) }
                  ?.add(deviceId)
          }
      }
  }
  return gameControllerDeviceIds
}

Java

 public ArrayList<Integer> getGameControllerIds() {
  ArrayList<Integer> gameControllerDeviceIds = new ArrayList<Integer>();
  int[] deviceIds = InputDevice.getDeviceIds();
  for (int deviceId : deviceIds) {
      InputDevice dev = InputDevice.getDevice(deviceId);

      if (dev == null) {
          continue;
      }

      // Verify that the device has gamepad buttons, control sticks, or both.
      if (dev.supportsSource(SOURCE_GAMEPAD) || dev.supportsSource(SOURCE_JOYSTICK)) {
          // This device is a game controller. Store its device ID.
          if (!gameControllerDeviceIds.contains(deviceId)) {
              gameControllerDeviceIds.add(deviceId);
          }
      }
  }
  return gameControllerDeviceIds;
}

कंट्रोलर के इनपुट को प्रोसेस करना

इस सेक्शन में, Android पर काम करने वाले गेम कंट्रोलर के टाइप के बारे में बताया गया है.

C++ डेवलपर को, गेम कंट्रोलर लाइब्रेरी का इस्तेमाल करना चाहिए. यह सभी कंट्रोलर को, सुविधाओं के सबसे सामान्य सबसेट में यूनीफ़ाई करती है. साथ ही, उनके बीच एक जैसा इंटरफ़ेस उपलब्ध कराती है. इसमें बटन लेआउट का पता लगाने की सुविधा भी शामिल है.

इस इमेज में दिखाया गया है कि Android गेम डेवलपर, Android पर किसी सामान्य कंट्रोलर के दिखने की उम्मीद कैसे कर सकता है.

डी-पैड, ऐनलॉग स्टिक, और बटन जैसे लेबल वाले इनपुट के साथ सामान्य गेम कंट्रोलर
इमेज 1. सामान्य गेम कंट्रोलर की प्रोफ़ाइल.

इस टेबल में, गेम कंट्रोलर के लिए स्टैंडर्ड इवेंट के नाम और टाइप की सूची दी गई है. इवेंट की पूरी सूची के लिए, सामान्य वैरिएंट देखें. सिस्टम MotionEvent इवेंट onGenericMotionEvent के ज़रिए और KeyEvent इवेंट onKeyDown और onKeyUp के ज़रिए भेजता है.

कंट्रोलर इनपुट KeyEvent MotionEvent
1. डी-पैड
AXIS_HAT_X
(हॉरिजॉन्टल इनपुट)
AXIS_HAT_Y
(वर्टिकल इनपुट)
2. लेफ़्ट ऐनालॉग स्टिक
KEYCODE_BUTTON_THUMBL
(दबाने पर)
AXIS_X
(हॉरिजॉन्टल मूवमेंट)
AXIS_Y
(वर्टिकल मूवमेंट)
3. राइट ऐनालॉग स्टिक
KEYCODE_BUTTON_THUMBR
(दबाने पर)
AXIS_Z
(हॉरिजॉन्टल मूवमेंट)
AXIS_RZ
(वर्टिकल मूवमेंट)
4. X बटन KEYCODE_BUTTON_X
5. A बटन KEYCODE_BUTTON_A
6. Y बटन KEYCODE_BUTTON_Y
7. B बटन KEYCODE_BUTTON_B
8. राइट बंपर
KEYCODE_BUTTON_R1
9. राइट ट्रिगर
AXIS_RTRIGGER
10. लेफ़्ट ट्रिगर AXIS_LTRIGGER
11. लेफ़्ट बंपर KEYCODE_BUTTON_L1
12. शुरू करें KEYCODE_BUTTON_START
13. चुनें KEYCODE_BUTTON_SELECT

बटन दबाने की कार्रवाई को हैंडल करना

Android, कंट्रोलर के बटन दबाने की कार्रवाई को कीबोर्ड के बटन दबाने की कार्रवाई की तरह ही रिपोर्ट करता है. इसलिए, आपको यह करना होगा:

  • पुष्टि करें कि इवेंट, SOURCE_GAMEPAD से आ रहा है.
  • पक्का करें कि आपको बटन दबाने की कार्रवाई की जानकारी सिर्फ़ एक बार मिले KeyEvent.getRepeatCount(), Android, कीबोर्ड की किसी कुंजी को दबाकर रखने पर, बार-बार की इवेंट भेजेगा.
  • यह बताने के लिए कि किसी इवेंट को हैंडल किया गया है, true वैल्यू दिखाएं.
  • हैंडल नहीं किए गए इवेंट को super पर पास करें, ताकि यह पुष्टि की जा सके कि Android की अलग-अलग कंपैटिबिलिटी लेयर सही तरीके से काम करती हैं.

    Kotlin

    class GameView : View {
    // ...
    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        event.apply {
            var handled = false
    
            // make sure we're handling gamepad events
            if (isFromSource(SOURCE_GAMEPAD)) {
    
                // avoid processing the keycode repeatedly
                if (repeatCount == 0) {
                    when (keyCode) {
                        // handle the "A" button
                        KEYCODE_BUTTON_A -> {
                          handled = true
                        }
                    }
                    // ...
                }
            }
            if (handled) {
                return true
            }
       }
       return super.onKeyDown(keyCode, event)
      }
    }
    

    Java

    public class GameView extends View {
    // ...
    
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        boolean handled = false;
        // make sure we're handling gamepad events
        if (event.isFromSource(SOURCE_GAMEPAD)) {
            // avoid processing the keycode repeatedly
            if (event.getRepeatCount() == 0) {
                switch (keyCode) {
                    case KEYCODE_BUTTON_A:
                        // handle the "A" button
                        handled = true;
                        break;
                    // ...
                }
            }
            // mark this event as handled
            if (handled) {
                return true;
            }
        }
        // Always do this instead of "return false"
        // it allows Android's input compatibility layers to work
        return super.onKeyDown(keyCode, event);
      }
    }
    

डायरेक्शनल पैड के इनपुट को प्रोसेस करना

4-वे डायरेक्शनल पैड या डी-पैड, कई गेम कंट्रोलर में एक सामान्य फ़िज़िकल कंट्रोल होता है. Android, डी-पैड के UP और DOWN बटन दबाने की कार्रवाई को AXIS_HAT_Y इवेंट के तौर पर रिपोर्ट करता है. इसमें -1.0 का मतलब है कि UP बटन दबाया गया है और 1.0 का मतलब है कि DOWN बटन दबाया गया है. यह डी-पैड के LEFT या RIGHT बटन दबाने की कार्रवाई को AXIS_HAT_X इवेंट के तौर पर रिपोर्ट करता है. इसमें -1.0 का मतलब है कि LEFT बटन दबाया गया है और 1.0 का मतलब है कि RIGHT बटन दबाया गया है.

कुछ कंट्रोलर, डी-पैड के बटन दबाने की कार्रवाई को की कोड के साथ रिपोर्ट करते हैं. अगर आपके गेम में डी-पैड के बटन दबाने की कार्रवाई अहम है, तो आपको हैट ऐक्सिस इवेंट और डी-पैड के की कोड को एक ही इनपुट इवेंट के तौर पर मानना चाहिए. ऐसा करने का सुझाव टेबल 2 में दिया गया है.

टेबल 2. डी-पैड के की कोड और हैट ऐक्सिस की वैल्यू के लिए, गेम की डिफ़ॉल्ट कार्रवाइयों के सुझाव.

गेम की कार्रवाई डी-पैड का की कोड हैट ऐक्सिस का कोड
ऊपर ले जाएं KEYCODE_DPAD_UP AXIS_HAT_Y (0 से -1.0 तक की वैल्यू के लिए)
नीचे ले जाएं KEYCODE_DPAD_DOWN AXIS_HAT_Y (0 से 1.0 तक की वैल्यू के लिए)
बाईं ओर ले जाएं KEYCODE_DPAD_LEFT AXIS_HAT_X (0 से -1.0 तक की वैल्यू के लिए)
दाईं ओर ले जाएं KEYCODE_DPAD_RIGHT AXIS_HAT_X (0 से 1.0 तक की वैल्यू के लिए)

यहां दिए गए कोड स्निपेट में, एक हेल्पर क्लास दिखाई गई है. इसकी मदद से, डी-पैड की दिशा तय करने के लिए, इनपुट इवेंट से हैट ऐक्सिस और की कोड की वैल्यू की जांच की जा सकती है.

Kotlin

class Dpad {

    private var directionPressed = -1 // initialized to -1

    fun getDirectionPressed(event: InputEvent): Int {
        if (!isDpadDevice(event)) {
            return -1
        }

        // If the input event is a MotionEvent, check its hat axis values.
        (event as? MotionEvent)?.apply {

            // Use the hat axis value to find the D-pad direction
            val xaxis: Float = event.getAxisValue(MotionEvent.AXIS_HAT_X)
            val yaxis: Float = event.getAxisValue(MotionEvent.AXIS_HAT_Y)

            directionPressed = when {
                // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
                // LEFT and RIGHT direction accordingly.
                xaxis.compareTo(-1.0f) == 0 -> Dpad.LEFT
                xaxis.compareTo(1.0f) == 0 -> Dpad.RIGHT
                // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
                // UP and DOWN direction accordingly.
                yaxis.compareTo(-1.0f) == 0 -> Dpad.UP
                yaxis.compareTo(1.0f) == 0 -> Dpad.DOWN
                else -> directionPressed
            }
        }
        // If the input event is a KeyEvent, check its key code.
        (event as? KeyEvent)?.apply {

            // Use the key code to find the D-pad direction.
            directionPressed = when(event.keyCode) {
                KeyEvent.KEYCODE_DPAD_LEFT -> Dpad.LEFT
                KeyEvent.KEYCODE_DPAD_RIGHT -> Dpad.RIGHT
                KeyEvent.KEYCODE_DPAD_UP -> Dpad.UP
                KeyEvent.KEYCODE_DPAD_DOWN -> Dpad.DOWN
                KeyEvent.KEYCODE_DPAD_CENTER ->  Dpad.CENTER
                else -> directionPressed
            }
        }
        return directionPressed
    }

    companion object {
        internal const val UP = 0
        internal const val LEFT = 1
        internal const val RIGHT = 2
        internal const val DOWN = 3
        internal const val CENTER = 4

        fun isDpadDevice(event: InputEvent): Boolean =
            // Check that input comes from a device with directional pads.
            return event.isFromSource(InputDevice.SOURCE_DPAD)
    }
}

Java

public class Dpad {
    final static int UP       = 0;
    final static int LEFT     = 1;
    final static int RIGHT    = 2;
    final static int DOWN     = 3;
    final static int CENTER   = 4;

    int directionPressed = -1; // initialized to -1

    public int getDirectionPressed(InputEvent event) {
        if (!isDpadDevice(event)) {
           return -1;
        }

        // If the input event is a MotionEvent, check its hat axis values.
        if (event instanceof MotionEvent) {

            // Use the hat axis value to find the D-pad direction
            MotionEvent motionEvent = (MotionEvent) event;
            float xaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X);
            float yaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y);

            // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
            // LEFT and RIGHT direction accordingly.
            if (Float.compare(xaxis, -1.0f) == 0) {
                directionPressed =  Dpad.LEFT;
            } else if (Float.compare(xaxis, 1.0f) == 0) {
                directionPressed =  Dpad.RIGHT;
            }
            // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
            // UP and DOWN direction accordingly.
            else if (Float.compare(yaxis, -1.0f) == 0) {
                directionPressed =  Dpad.UP;
            } else if (Float.compare(yaxis, 1.0f) == 0) {
                directionPressed =  Dpad.DOWN;
            }
        }

        // If the input event is a KeyEvent, check its key code.
        else if (event instanceof KeyEvent) {

           // Use the key code to find the D-pad direction.
            KeyEvent keyEvent = (KeyEvent) event;
            if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
                directionPressed = Dpad.LEFT;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
                directionPressed = Dpad.RIGHT;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) {
                directionPressed = Dpad.UP;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
                directionPressed = Dpad.DOWN;
            } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
                directionPressed = Dpad.CENTER;
            }
        }
        return directionPressed;
    }

    public static boolean isDpadDevice(InputEvent event) {
        // Check that input comes from a device with directional pads.
        return event.isFromSource(InputDevice.SOURCE_DPAD);
     }
}

इस हेल्पर क्लास का इस्तेमाल, अपने गेम में कहीं भी किया जा सकता है. जैसे, onGenericMotionEvent() या onKeyDown() कॉलबैक में, जहां आपको डी-पैड के इनपुट को प्रोसेस करना है.

उदाहरण के लिए:

Kotlin

private val dpad = Dpad()
...
override fun onGenericMotionEvent(event: MotionEvent): Boolean {
    if (Dpad.isDpadDevice(event)) {
        when (dpad.getDirectionPressed(event)) {
            Dpad.LEFT -> {
                // Do something for LEFT direction press
                ...
                return true
            }
            Dpad.RIGHT -> {
                // Do something for RIGHT direction press
                ...
                return true
            }
            Dpad.UP -> {
                // Do something for UP direction press
                ...
                return true
            }
            ...
        }
    }

    // Check if this event is from a joystick movement and process accordingly.
    ...
}

Java

Dpad dpad = new Dpad();
...
@Override
public boolean onGenericMotionEvent(MotionEvent event) {

    // Check if this event if from a D-pad and process accordingly.
    if (Dpad.isDpadDevice(event)) {

       int press = dpad.getDirectionPressed(event);
       switch (press) {
            case LEFT:
                // Do something for LEFT direction press
                ...
                return true;
            case RIGHT:
                // Do something for RIGHT direction press
                ...
                return true;
            case UP:
                // Do something for UP direction press
                ...
                return true;
            ...
        }
    }

    // Check if this event is from a joystick movement and process accordingly.
    ...
}

जॉयस्टिक के मूवमेंट को प्रोसेस करना

जब खिलाड़ी अपने गेम कंट्रोलर पर जॉयस्टिक को घुमाते हैं, तो Android एक MotionEvent रिपोर्ट करता है. इसमें ACTION_MOVE ऐक्शन कोड और जॉयस्टिक के ऐक्सिस की अपडेट की गई पोज़िशन शामिल होती हैं. `MotionEvent` से मिले डेटा का इस्तेमाल करके, आपका गेम यह तय कर सकता है कि जॉयस्टिक का वह मूवमेंट हुआ है या नहीं जो उसके लिए अहम है.MotionEvent

ध्यान दें कि जॉयस्टिक के मोशन इवेंट, एक ही ऑब्जेक्ट में कई मूवमेंट सैंपल को एक साथ बैच कर सकते हैं. The MotionEvent ऑब्जेक्ट में, हर जॉयस्टिक ऐक्सिस की मौजूदा पोज़िशन के साथ-साथ, हर ऐक्सिस के लिए कई पिछली पोज़िशन भी शामिल होती हैं. Android, ऐक्शन कोड ACTION_MOVE वाले मोशन इवेंट (जैसे, जॉयस्टिक के मूवमेंट) की रिपोर्ट करते समय, ऐक्सिस की वैल्यू को बैच करता है, ताकि परफ़ॉर्मेंस बेहतर हो सके. किसी ऐक्सिस की पिछली वैल्यू में, मौजूदा ऐक्सिस वैल्यू से पुरानी और पहले के किसी भी मोशन इवेंट में रिपोर्ट की गई वैल्यू से नई, अलग-अलग वैल्यू का सेट शामिल होता है. ज़्यादा जानकारी के लिए, MotionEvent रेफ़रंस देखें.

जॉयस्टिक के इनपुट के आधार पर, गेम ऑब्जेक्ट के मूवमेंट को सटीक तरीके से रेंडर करने के लिए, MotionEvent ऑब्जेक्ट से मिली पिछली जानकारी का इस्तेमाल किया जा सकता है.

मौजूदा और पिछली वैल्यू पाने के लिए, इन तरीकों का इस्तेमाल किया जा सकता है:

यहां दिए गए स्निपेट में, जॉयस्टिक के इनपुट को प्रोसेस करने के लिए, onGenericMotionEvent() कॉलबैक को ओवरराइड करने का तरीका दिखाया गया है. आपको सबसे पहले किसी ऐक्सिस की पिछली वैल्यू को प्रोसेस करना चाहिए. इसके बाद, उसकी मौजूदा पोज़िशन को प्रोसेस करना चाहिए.

Kotlin

class GameView(...) : View(...) {

    override fun onGenericMotionEvent(event: MotionEvent): Boolean {

        // Check that the event came from a game controller
        return if (event.source and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK
                && event.action == MotionEvent.ACTION_MOVE) {

            // Process the movements starting from the
            // earliest historical position in the batch
            (0 until event.historySize).forEach { i ->
                // Process the event at historical position i
                processJoystickInput(event, i)
            }

            // Process the current movement sample in the batch (position -1)
            processJoystickInput(event, -1)
            true
        } else {
            super.onGenericMotionEvent(event)
        }
    }
}

Java

public class GameView extends View {

    @Override
    public boolean onGenericMotionEvent(MotionEvent event) {

        // Check that the event came from a game controller
        if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) ==
                InputDevice.SOURCE_JOYSTICK &&
                event.getAction() == MotionEvent.ACTION_MOVE) {

            // Process all historical movement samples in the batch
            final int historySize = event.getHistorySize();

            // Process the movements starting from the
            // earliest historical position in the batch
            for (int i = 0; i < historySize; i++) {
                // Process the event at historical position i
                processJoystickInput(event, i);
            }

            // Process the current movement sample in the batch (position -1)
            processJoystickInput(event, -1);
            return true;
        }
        return super.onGenericMotionEvent(event);
    }
}

जॉयस्टिक के इनपुट का इस्तेमाल करने से पहले, आपको यह तय करना होगा कि जॉयस्टिक सेंटर में है या नहीं. इसके बाद, उसके ऐक्सिस के मूवमेंट की गिनती करनी होगी. जॉयस्टिक में आम तौर पर, फ़्लैट एरिया होता है. इसका मतलब है कि (0, 0) कोऑर्डिनेट के आस-पास वैल्यू की एक रेंज होती है. इस रेंज में,ऐक्सिस को सेंटर में माना जाता है. अगर Android से रिपोर्ट की गई ऐक्सिस की वैल्यू, फ़्लैट एरिया में आती है, तो आपको कंट्रोलर को रेस्ट पर मानना चाहिए. इसका मतलब है कि दोनों ऐक्सिस के साथ कोई मूवमेंट नहीं हो रहा है.

इस स्निपेट में, हर ऐक्सिस के साथ होने वाले मूवमेंट की गिनती करने का एक हेल्पर तरीका दिखाया गया है. इस हेल्पर को, processJoystickInput() तरीके में लागू किया जाता है. इसके बारे में, यहां दिए गए सैंपल में ज़्यादा जानकारी दी गई है:

Kotlin

private fun getCenteredAxis(
        event: MotionEvent,
        device: InputDevice,
        axis: Int,
        historyPos: Int
): Float {
    val range: InputDevice.MotionRange? = device.getMotionRange(axis, event.source)

    // A joystick at rest does not always report an absolute position of
    // (0,0). Use the getFlat() method to determine the range of values
    // bounding the joystick axis center.
    range?.apply {
        val value: Float = if (historyPos < 0) {
            event.getAxisValue(axis)
        } else {
            event.getHistoricalAxisValue(axis, historyPos)
        }

        // Ignore axis values that are within the 'flat' region of the
        // joystick axis center.
        if (Math.abs(value) > flat) {
            return value
        }
    }
    return 0f
}

Java

private static float getCenteredAxis(MotionEvent event,
        InputDevice device, int axis, int historyPos) {
    final InputDevice.MotionRange range =
            device.getMotionRange(axis, event.getSource());

    // A joystick at rest does not always report an absolute position of
    // (0,0). Use the getFlat() method to determine the range of values
    // bounding the joystick axis center.
    if (range != null) {
        final float flat = range.getFlat();
        final float value =
                historyPos < 0 ? event.getAxisValue(axis):
                event.getHistoricalAxisValue(axis, historyPos);

        // Ignore axis values that are within the 'flat' region of the
        // joystick axis center.
        if (Math.abs(value) > flat) {
            return value;
        }
    }
    return 0;
}

यहां बताया गया है कि अपने गेम में जॉयस्टिक के मूवमेंट को कैसे प्रोसेस किया जा सकता है:

Kotlin

private fun processJoystickInput(event: MotionEvent, historyPos: Int) {

    val inputDevice = event.device

    // Calculate the horizontal distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat axis, or the right control stick.
    var x: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_X, historyPos)
    if (x == 0f) {
        x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_X, historyPos)
    }
    if (x == 0f) {
        x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Z, historyPos)
    }

    // Calculate the vertical distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat switch, or the right control stick.
    var y: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Y, historyPos)
    if (y == 0f) {
        y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_Y, historyPos)
    }
    if (y == 0f) {
        y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_RZ, historyPos)
    }

    // Update the ship object based on the new x and y values
}

Java

private void processJoystickInput(MotionEvent event,
        int historyPos) {

    InputDevice inputDevice = event.getDevice();

    // Calculate the horizontal distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat axis, or the right control stick.
    float x = getCenteredAxis(event, inputDevice,
            MotionEvent.AXIS_X, historyPos);
    if (x == 0) {
        x = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_HAT_X, historyPos);
    }
    if (x == 0) {
        x = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_Z, historyPos);
    }

    // Calculate the vertical distance to move by
    // using the input value from one of these physical controls:
    // the left control stick, hat switch, or the right control stick.
    float y = getCenteredAxis(event, inputDevice,
            MotionEvent.AXIS_Y, historyPos);
    if (y == 0) {
        y = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_HAT_Y, historyPos);
    }
    if (y == 0) {
        y = getCenteredAxis(event, inputDevice,
                MotionEvent.AXIS_RZ, historyPos);
    }

    // Update the ship object based on the new x and y values
}

एक से ज़्यादा जॉयस्टिक वाले गेम कंट्रोलर के लिए, यहां दिए गए सबसे सही तरीके अपनाएं:

  • डुअल कंट्रोलर स्टिक को हैंडल करें. कई गेम कंट्रोलर में, लेफ़्ट और राइट, दोनों जॉयस्टिक होती हैं. लेफ़्ट स्टिक के लिए, Android हॉरिजॉन्टल मूवमेंट को AXIS_X इवेंट और वर्टिकल मूवमेंट को AXIS_Y इवेंट के तौर पर रिपोर्ट करता है. राइट स्टिक के लिए, Android हॉरिजॉन्टल मूवमेंट को AXIS_Z इवेंट और वर्टिकल मूवमेंट को AXIS_RZ इवेंट के तौर पर रिपोर्ट करता है. पक्का करें कि आपके कोड में, दोनों कंट्रोलर स्टिक को हैंडल किया गया हो.
  • शोल्डर ट्रिगर के बटन दबाने की कार्रवाई को हैंडल करें. साथ ही, पक्का करें कि आपका गेम, AXIS_ और KEYCODE_BUTTON_ इवेंट के साथ काम करता हो. कुछ कंट्रोलर में, लेफ़्ट और राइट शोल्डर ट्रिगर होते हैं. जब ये ट्रिगर मौजूद होते हैं, तो ये AXIS_*TRIGGER या KEYCODE_BUTTON_*2 इवेंट या दोनों एमिट करते हैं. लेफ़्ट ट्रिगर के लिए, यह AXIS_LTRIGGER और KEYCODE_BUTTON_L2 होगा. राइट ट्रिगर के लिए, यह AXIS_RTRIGGER और KEYCODE_BUTTON_R2होगा. ऐक्सिस इवेंट सिर्फ़ तब होते हैं, जब ट्रिगर 0 और 1 के बीच वैल्यू की रेंज एमिट करता है. साथ ही, ऐनालॉग आउटपुट वाले कुछ कंट्रोलर, ऐक्सिस इवेंट के अलावा बटन इवेंट भी एमिट करते हैं. सभी सामान्य गेम कंट्रोलर के साथ कंपैटिबिलिटी बनाए रखने के लिए, गेम में AXIS_ और KEYCODE_BUTTON_ दोनों इवेंट काम करने चाहिए. हालांकि, अगर कोई कंट्रोलर दोनों इवेंट की रिपोर्ट करता है, तो उस इवेंट को प्राथमिकता दें जो आपके गेमप्ले के लिए सबसे सही हो. Android 4.3 (एपीआई लेवल 18) और इसके बाद के वर्शन पर, AXIS_LTRIGGER जनरेट करने वाला कंट्रोलर, AXIS_BRAKE ऐक्सिस के लिए भी एक जैसी वैल्यू रिपोर्ट करता है. AXIS_RTRIGGER और AXIS_GAS के लिए भी यही नियम लागू होता है. Android, सभी ऐनालॉग ट्रिगर के बटन दबाने की कार्रवाई को, 0.0 (छोड़ने पर) से 1.0 (पूरी तरह दबाने पर) तक की सामान्य वैल्यू के साथ रिपोर्ट करता है.
  • एम्युलेट किए गए एनवायरमेंट में, खास व्यवहार और सहायता अलग-अलग हो सकती है. एम्युलेट किए गए प्लैटफ़ॉर्म, जैसे कि Google Play Games, होस्ट ऑपरेटिंग सिस्टम की क्षमताओं के आधार पर, व्यवहार में थोड़ा अंतर दिखा सकते हैं. उदाहरण के लिए, AXIS_ और KEYCODE_BUTTON_ दोनों इवेंट एमिट करने वाले कुछ कंट्रोलर, सिर्फ़ AXIS_ इवेंट एमिट करते हैं. साथ ही, कुछ कंट्रोलर के लिए सहायता पूरी तरह से उपलब्ध नहीं हो सकती है.

सामान्य वैरिएंट

Android में कंट्रोलर के लिए कई तरह की सहायता उपलब्ध है. ऐसे में, यह समझना मुश्किल हो सकता है कि अपने गेम को कैसे बनाया और टेस्ट किया जाए, ताकि यह पुष्टि की जा सके कि वह आपके खिलाड़ियों के लिए बिना किसी गड़बड़ी के काम करता है. हमारा मानना है कि इस तरह की अलग-अलग सहायता उपलब्ध होने के बावजूद, दुनिया भर के कंट्रोलर बनाने वाली कंपनियां, कंट्रोलर की तीन अलग-अलग स्टाइल का लगातार पालन करती हैं. कुछ कंपनियां, इनमें से दो या उससे ज़्यादा स्टाइल के बीच हार्डवेयर टॉगल उपलब्ध कराती हैं.

इसका मतलब है कि आपकी डेवलपर टीम, सिर्फ़ तीन कंट्रोलर के साथ टेस्ट कर सकती है. साथ ही, उसे इस बात का भरोसा हो सकता है कि आपका गेम, अनुमति देने और अस्वीकार करने की सूचियों का इस्तेमाल किए बिना खेला जा सकता है.

कंट्रोलर के सामान्य टाइप

कंट्रोलर की सबसे सामान्य स्टाइल, लोकप्रिय गेम कंसोल के लेआउट की तरह होती है. यह बटन के लेबल और लेआउट के साथ-साथ, फ़ंक्शनल भी होती है. इससे यह पता चलता है कि कौनसे इवेंट ट्रिगर होते हैं. अलग-अलग कंसोल टाइप के बीच हार्डवेयर टॉगल वाले कंट्रोलर, भेजे जाने वाले इवेंट और अक्सर उनके लॉजिकल बटन लेआउट को भी बदल देंगे.

हमारा सुझाव है कि टेस्ट करते समय, पुष्टि करें कि आपका गेम, हर कैटगरी में मौजूद एक कंट्रोलर के साथ काम करता है. आपके पास पहले पक्ष के कंट्रोलर या तीसरे पक्ष के लोकप्रिय निर्माताओं के कंट्रोलर के साथ टेस्ट करने का विकल्प होता है. आम तौर पर, हम सबसे लोकप्रिय कंट्रोलर को ऊपर दी गई परिभाषा के हिसाब से मैप करने की पूरी कोशिश करेंगे.

कंट्रोलर का टाइप व्यवहार में अंतर लेबलिंग में अंतर
Xbox स्टाइल वाले कंट्रोलर

ये कंट्रोलर आम तौर पर, Microsoft Xbox और Windows* प्लैटफ़ॉर्म के लिए बनाए जाते हैं.

ये कंट्रोलर, कंट्रोलर के इनपुट को प्रोसेस करना में बताए गए फ़ीचर सेट से मेल खाते हैं इन कंट्रोलर पर मौजूद L2/R2 बटन को LT/RT लेबल किया जाता है
Switch स्टाइल वाले कंट्रोलर

ये कंट्रोलर आम तौर पर, Nintendo Switch* कंसोल के परिवार के लिए डिज़ाइन किए जाते हैं.

ये कंट्रोलर, KeyEvent KEYCODE_BUTTON_R2 KEYCODE_BUTTON_L2 MotionEvent भेजते हैं इन कंट्रोलर पर मौजूद L2/R2 बटन को ZL/ZR लेबल किया जाता है.

ये कंट्रोलर, A और B बटन के साथ-साथ X और Y बटन को भी स्वैप करते हैं. इसलिए, KEYCODE_BUTTON_A को B लेबल किया जाता है और इसके उलट भी ऐसा ही होता है.

PlayStation स्टाइल वाले कंट्रोलर

ये कंट्रोलर आम तौर पर, Sony PlayStation* कंसोल के परिवार के लिए डिज़ाइन किए जाते हैं.

ये कंट्रोलर, MotionEvents **Xbox स्टाइल वाले कंट्रोलर** की तरह भेजते हैं. हालांकि, पूरी तरह से दबाने पर, KeyEvents **Switch स्टाइल वाले कंट्रोलर** की तरह भी भेजते हैं. ये कंट्रोलर, फ़ेस बटन के लिए ग्लिफ़ का अलग सेट इस्तेमाल करते हैं.

* Microsoft, Xbox, और Windows, Microsoft के रजिस्टर किए हुए ट्रेडमार्क हैं. Nintendo Switch, Nintendo of America Inc. का रजिस्टर किया हुआ ट्रेडमार्क है. PlayStation, Sony Interactive Entertainment Inc. का रजिस्टर किया हुआ ट्रेडमार्क है.

ट्रिगर बटन में अंतर करना

कुछ कंट्रोलर, AXIS_LTRIGGER और AXIS_RTRIGGER भेजते हैं. कुछ KEYCODE_BUTTON_L2 और KEYCODE_BUTTON_R2 भेजते हैं. वहीं, अन्य कंट्रोलर, अपने हार्डवेयर की क्षमताओं के आधार पर ये सभी इवेंट भेजते हैं. इन सभी इवेंट के लिए सहायता उपलब्ध कराकर, कंपैटिबिलिटी को ज़्यादा से ज़्यादा बढ़ाएं.

AXIS_LTRIGGER भेजने वाले सभी कंट्रोलर, AXIS_BRAKE भी भेजेंगे. इसी तरह, AXIS_RTRIGGER और AXIS_GAS भी भेजेंगे. इससे, रेसिंग व्हील और सामान्य गेम कंट्रोलर के बीच कंपैटिबिलिटी को ज़्यादा से ज़्यादा बढ़ाने में मदद मिलेगी. आम तौर पर, इससे कोई समस्या नहीं होगी. हालांकि, की रीमैपिंग स्क्रीन जैसी सुविधाओं के लिए, इस बात का ध्यान रखें.

ट्रिगर MotionEvent KeyEvent
लेफ़्ट ट्रिगर AXIS_LTRIGGER
AXIS_BRAKE
KEYCODE_BUTTON_L2
राइट ट्रिगर AXIS_RTRIGGER
AXIS_GAS
KEYCODE_BUTTON_R2

यह पक्का करें कि आपका गेम, KeyEvent और MotionEvent दोनों को हैंडल कर सकता है. इससे, ज़्यादा से ज़्यादा कंट्रोलर के साथ कंपैटिबिलिटी बनाए रखने में मदद मिलेगी. साथ ही, यह भी पक्का करें कि इवेंट डुप्लीकेट न हों.

इस्तेमाल किए जा सकने वाले कंट्रोलर

हमारा सुझाव है कि टेस्ट करते समय, पुष्टि करें कि आपका गेम, हर कैटगरी में मौजूद एक कंट्रोलर के साथ काम करता है.

  • Xbox स्टाइल
  • Nintendo Switch स्टाइल
  • PlayStation स्टाइल

पहले पक्ष के कंट्रोलर या तीसरे पक्ष के लोकप्रिय निर्माताओं के कंट्रोलर के साथ टेस्ट किया जा सकता है. आम तौर पर, हम सबसे लोकप्रिय कंट्रोलर को परिभाषा के हिसाब से मैप करने की पूरी कोशिश करते हैं.