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

सिस्टम लेवल पर, Android गेम कंट्रोलर से मिले इनपुट इवेंट को Android कीवर्ड कोड और ऐक्सिस वैल्यू के तौर पर रिपोर्ट करता है. अपने गेम में, आपको ये कोड और वैल्यू मिल सकती हैं और उन्हें खास इन-गेम ऐक्शन में बदला जा सकता है.

जब खिलाड़ी अपने Android डिवाइसों से गेम कंट्रोलर को कनेक्ट करते हैं या वायरलेस तरीके से जोड़ते हैं, तो सिस्टम कंट्रोलर को इनपुट डिवाइस के तौर पर अपने-आप पहचान लेता है और उसके इनपुट इवेंट की रिपोर्टिंग शुरू कर देता है. आपके गेम को ये इनपुट इवेंट पाने के लिए, अपने ऐक्टिव Activity या फ़ोकस View में इन कॉलबैक के तरीकों का इस्तेमाल किया जा सकता है. आपको Activity या View में से किसी एक के लिए कॉलबैक लागू करना चाहिए, लेकिन दोनों के लिए नहीं:

  • Activity ने ये मैसेज भेजे हैं:
    • dispatchGenericMotionEvent(android.view. MotionEvent)

      इसे सामान्य मोशन इवेंट को प्रोसेस करने के लिए कॉल किया जाता है, जैसे कि जॉयस्टिक की मूवमेंट.

    • dispatchKeyEvent(android.view.KeyEvent)

      गेमपैड या D-पैड बटन को दबाने या छोड़ने जैसे मुख्य इवेंट को प्रोसेस करने के लिए कॉल किया जाता है.

  • View ने ये मैसेज भेजे हैं:
    • onGenericMotionEvent(android.view.MotionEvent)

      इसे सामान्य मोशन इवेंट को प्रोसेस करने के लिए कॉल किया जाता है, जैसे कि जॉयस्टिक की मूवमेंट.

    • onKeyDown(int, android.view.KeyEvent)

      इसे गेमपैड या डी-पैड बटन जैसे फ़िज़िकल बटन को दबाने के लिए कहा जाता है.

    • onKeyUp(int, android.view.KeyEvent)

      गेमपैड या D-पैड बटन जैसे किसी फ़िज़िकल बटन को रिलीज़ करने के लिए कॉल किया जाता है.

हमारा सुझाव है कि आप उस View ऑब्जेक्ट से इवेंट कैप्चर करें जिससे उपयोगकर्ता इंटरैक्ट करता है. मिले इनपुट इवेंट के टाइप के बारे में जानकारी पाने के लिए, कॉलबैक से मिले इन ऑब्जेक्ट की जांच करें:

KeyEvent
ऐसा ऑब्जेक्ट जो डायरेक्शनल पैड (D-pad) और गेमपैड बटन इवेंट के बारे में बताता है. मुख्य इवेंट के साथ एक मुख्य कोड होता है, जो ट्रिगर किए गए किसी खास बटन के बारे में बताता है, जैसे कि DPAD_DOWN या BUTTON_A. getKeyCode() को कॉल करके या onKeyDown() जैसे कीवर्ड इवेंट कॉलबैक से, कीवर्ड कोड पाया जा सकता है.
MotionEvent
जॉयस्टिक और कंधे के ट्रिगर की गतिविधियों से मिलने वाले इनपुट की जानकारी देने वाला ऑब्जेक्ट. मोशन इवेंट के साथ एक ऐक्शन कोड और ऐक्सिस की वैल्यू का एक सेट होता है. ऐक्शन कोड से, डिवाइस की स्थिति में हुए बदलाव के बारे में पता चलता है. जैसे, जॉयस्टिक को हिलाया जा रहा है. ऐक्सिस वैल्यू, किसी फ़िज़िकल कंट्रोल की स्थिति और गति की अन्य प्रॉपर्टी के बारे में बताती हैं. जैसे, AXIS_X या AXIS_RTRIGGER. getAxisValue() को कॉल करके, getAction() और ऐक्सिस की वैल्यू को कॉल करके, ऐक्शन कोड पाया जा सकता है.

इस लेसन में, गेम स्क्रीन पर सबसे सामान्य तरह के फ़िज़िकल कंट्रोल (गेमपैड बटन, डायरेक्शनल पैड, और जॉयस्टिक) से मिलने वाले इनपुट को मैनेज करने का तरीका बताया गया है. इसके लिए, ऊपर बताए गए View कॉलबैक तरीके और KeyEvent और MotionEvent ऑब्जेक्ट को प्रोसेस करने का तरीका अपनाया गया है.

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

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

यह पुष्टि करने के लिए कि कनेक्ट किया गया इनपुट डिवाइस, गेम कंट्रोलर है या नहीं, उस डिवाइस पर काम करने वाले इनपुट सोर्स टाइप का एक बिट फ़ील्ड पाने के लिए getSources() को कॉल करें. इसके बाद, यह जांच की जा सकती है कि ये फ़ील्ड सेट हैं या नहीं:

  • सोर्स टाइप के SOURCE_GAMEPAD से पता चलता है कि इनपुट डिवाइस में गेमपैड बटन हैं. उदाहरण के लिए, 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 (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD
                    || sources and InputDevice.SOURCE_JOYSTICK == InputDevice.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);
        int sources = dev.getSources();

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

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

यह पता लगाने के लिए कि कनेक्ट किए गए गेम कंट्रोलर में कोई खास बटन कोड या ऐक्सिस कोड काम करता है या नहीं, इन तरीकों का इस्तेमाल करें:

  • Android 4.4 (एपीआई लेवल 19) या उसके बाद के वर्शन में, hasKeys(int...) को कॉल करके यह पता लगाया जा सकता है कि कनेक्ट किए गए गेम कंट्रोलर पर कोई कुंजी कोड काम करता है या नहीं.
  • Android 3.1 (एपीआई लेवल 12) या इसके बाद के वर्शन में, कनेक्ट किए गए गेम कंट्रोलर पर काम करने वाले सभी ऐक्सिस को ढूंढा जा सकता है. इसके लिए, पहले getMotionRanges() को कॉल करें. इसके बाद, दिखाए गए हर InputDevice.MotionRange ऑब्जेक्ट के अक्ष का आईडी पाने के लिए, getAxis() को कॉल करें.

गेमपैड के बटन दबाने की प्रोसेस

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

पहला डायग्राम. सामान्य गेम कंट्रोलर के लिए प्रोफ़ाइल.

इस इमेज में दिए गए कॉलआउट इन चीज़ों के बारे में बताते हैं:

गेमपैड के बटन दबाने पर जनरेट होने वाले सामान्य की कोड में ये शामिल हैं: BUTTON_A, BUTTON_B, BUTTON_SELECT, और BUTTON_START. कुछ गेम कंट्रोलर में, D-पैड क्रॉसबार के बीच में मौजूद बटन को दबाने पर भी DPAD_CENTER की कोड ट्रिगर होता है. आपका गेम, getKeyCode() को कॉल करके या onKeyDown() जैसे मुख्य इवेंट कॉलबैक से, मुख्य कोड की जांच कर सकता है. अगर यह आपके गेम से जुड़े किसी इवेंट को दिखाता है, तो उसे गेम ऐक्शन के तौर पर प्रोसेस करें. टेबल 1 में, गेमपैड के सबसे सामान्य बटन के लिए, गेम में की जाने वाली कार्रवाइयों के सुझाव दिए गए हैं.

टेबल 1. गेमपैड के बटन के लिए सुझाई गई गेम ऐक्शन.

गेम ऐक्शन बटन का कोड
मुख्य मेन्यू में जाकर गेम शुरू करना या गेम के दौरान उसे रोकना/अनरोकना BUTTON_START*
मेन्यू दिखाएं BUTTON_SELECT* और KEYCODE_MENU*
नेविगेशन डिज़ाइन गाइड में मौजूद Android के वापस जाएं नेविगेशन के व्यवहार के बारे में बताया गया है. KEYCODE_BACK
मेन्यू में किसी पिछले आइटम पर वापस जाना BUTTON_B
चुने गए विकल्प की पुष्टि करें या गेम में मुख्य कार्रवाई करें BUTTON_A और DPAD_CENTER

* आपके गेम में 'शुरू करें', 'चुनें' या 'मेन्यू' बटन मौजूद होने पर भरोसा नहीं किया जाना चाहिए.

सलाह: अपने गेम में कॉन्फ़िगरेशन स्क्रीन उपलब्ध कराएं, ताकि उपयोगकर्ता गेम ऐक्शन के लिए, गेम कंट्रोलर की मैपिंग को अपनी पसंद के मुताबिक बना सकें.

नीचे दिए गए स्निपेट में बताया गया है कि BUTTON_A और DPAD_CENTER बटन को दबाने पर होने वाली गेम ऐक्शन को जोड़ने के लिए, onKeyDown() को कैसे बदला जा सकता है.

Kotlin

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

    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        var handled = false
        if (event.source and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD) {
            if (event.repeatCount == 0) {
                when (keyCode) {
                    // Handle gamepad and D-pad button presses to navigate the ship
                    ...

                    else -> {
                        keyCode.takeIf { isFireKey(it) }?.run {
                            // Update the ship object to fire lasers
                            ...
                            handled = true
                        }
                    }
                }
            }
            if (handled) {
                return true
            }
        }
        return super.onKeyDown(keyCode, event)
    }

    // Here we treat Button_A and DPAD_CENTER as the primary action
    // keys for the game.
    private fun isFireKey(keyCode: Int): Boolean =
            keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_BUTTON_A
}

Java

public class GameView extends View {
    ...

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        boolean handled = false;
        if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)
                == InputDevice.SOURCE_GAMEPAD) {
            if (event.getRepeatCount() == 0) {
                switch (keyCode) {
                    // Handle gamepad and D-pad button presses to
                    // navigate the ship
                    ...

                    default:
                         if (isFireKey(keyCode)) {
                             // Update the ship object to fire lasers
                             ...
                             handled = true;
                         }
                     break;
                }
            }
            if (handled) {
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    private static boolean isFireKey(int keyCode) {
        // Here we treat Button_A and DPAD_CENTER as the primary action
        // keys for the game.
        return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
                || keyCode == KeyEvent.KEYCODE_BUTTON_A;
    }
}

ध्यान दें: Android 4.2 (एपीआई लेवल 17) और उससे पहले के वर्शन पर, सिस्टम डिफ़ॉल्ट रूप से BUTTON_A को Android Back बटन के तौर पर इस्तेमाल करता है. अगर आपका ऐप्लिकेशन, Android के इन वर्शन पर काम करता है, तो BUTTON_A को गेम की मुख्य कार्रवाई के तौर पर इस्तेमाल करें. डिवाइस पर मौजूदा Android SDK वर्शन का पता लगाने के लिए, Build.VERSION.SDK_INT वैल्यू देखें.

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

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

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

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

गेम ऐक्शन डी-पैड कुंजी कोड Hat Axis Code
ऊपर ले जाएं 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.
            event.source and InputDevice.SOURCE_DPAD != 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.
        if ((event.getSource() & InputDevice.SOURCE_DPAD)
             != InputDevice.SOURCE_DPAD) {
             return true;
         } else {
             return false;
         }
     }
}

इस हेल्पर क्लास का इस्तेमाल, अपने गेम में जहां भी डी-पैड इनपुट को प्रोसेस करना हो वहां किया जा सकता है. उदाहरण के लिए, 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 रेफ़रंस देखें.

गेम में किसी ऑब्जेक्ट की गति को ज़्यादा सटीक तरीके से रेंडर करने के लिए, पुरानी जानकारी का इस्तेमाल किया जा सकता है. यह जानकारी, जॉयस्टिक इनपुट के आधार पर मिलती है. मौजूदा और पुरानी वैल्यू पाने के लिए, getAxisValue() या getHistoricalAxisValue() को कॉल करें. getHistorySize() को कॉल करके, जॉयस्टिक इवेंट में पुराने पॉइंट की संख्या भी देखी जा सकती है.

नीचे दिए गए स्निपेट में बताया गया है कि जॉयस्टिक इनपुट को प्रोसेस करने के लिए, 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_ इवेंट उत्सर्जित करते हैं. साथ ही, हो सकता है कि कुछ कंट्रोलर के लिए पूरी तरह से सहायता उपलब्ध न हो.