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

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

  • 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_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 पर सामान्य कंट्रोलर कैसा दिखेगा.

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

इस टेबल में, गेम कंट्रोलर के लिए स्टैंडर्ड इवेंट के नाम और टाइप दिए गए हैं. इवेंट की पूरी सूची के लिए, सामान्य वैरिएंट देखें. सिस्टम, 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 से आ रहा है.
  • पक्का करें कि आपको बटन सिर्फ़ एक बार मिले. ऐसा न होने पर, Android, कीबोर्ड के किसी बटन को दबाकर रखने पर होने वाले इवेंट की तरह ही, मुख्य इवेंट को बार-बार भेजेगा.KeyEvent.getRepeatCount()
  • 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);
      }
    }
    

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

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

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

आम तौर पर, इन कंट्रोलर को 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* कंसोल के लिए डिज़ाइन किया जाता है.

ये कंट्रोलर, MotionEvent जैसे Xbox स्टाइल वाले कंट्रोलर भेजते हैं. हालांकि, पूरी तरह से दबाए जाने पर, ये KeyEvent जैसे 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 भी भेजेंगे, ताकि रेसिंग व्हील और सामान्य गेम कंट्रोलर के बीच ज़्यादा से ज़्यादा कंपैटिबिलिटी हो. आम तौर पर, इससे कोई समस्या नहीं होगी. हालांकि, बटन रीमैप करने की स्क्रीन जैसी सुविधाओं के लिए सावधान रहें.

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

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

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

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

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

पहले पक्ष (ग्राहक) के कंट्रोलर या तीसरे पक्ष के लोकप्रिय मैन्युफ़ैक्चरर के साथ टेस्ट किया जा सकता है. हम आम तौर पर, सबसे लोकप्रिय कंट्रोलर को परिभाषा के हिसाब से मैप करते हैं.