التعامل مع إجراءات وحدة التحكّم

على مستوى النظام، يُبلغ نظام Android عن إدخال رموز الأحداث من وحدات التحكّم في الألعاب كرموز مفاتيح Android وقيم المحاور. يمكنك تلقّي هذه الرموز في لعبتك. وقيمها وتحويلها إلى إجراءات محددة داخل اللعبة.

عندما يتصل اللاعبون بوحدة تحكم في الألعاب أو إقرانها لاسلكيًا أجهزتهم التي تعمل بنظام التشغيل Android، يكتشف النظام تلقائيًا وحدة التحكم كجهاز إدخال ويبدأ في الإبلاغ عن أحداث الإدخال. يمكن أن تحصل لعبتك على أحداث الإدخال هذه من خلال تنفيذ طرق معاودة الاتصال التالية في Activity أو View التركيز (يجب عليك تنفيذ عمليات الاستدعاء إما Activity أو View، ولكن ليس كليهما):

والمنهج الموصى به هو تسجيل الأحداث من كائن View محدد يتفاعل معه المستخدم. يمكنك فحص الكائنات التالية التي توفِّرها عمليات الاستدعاء للحصول على معلومات. حول نوع حدث الإدخال الذي تم تلقّيه:

KeyEvent
كائن يصف الاتجاهات لوحة التحكّم (D-pad) وأزرار التحكّم في الألعاب. تصاحب الأحداث الرئيسية key code الذي يشير إلى الزر المحدّد الذي تم تشغيله، مثل DPAD_DOWN أو BUTTON_A. يمكنك الحصول على رمز المفتاح من خلال طلب الرقم getKeyCode() أو من المفتاح. عمليات معاودة الاتصال للأحداث، مثل onKeyDown()
MotionEvent
كائن يصف الإدخال من ذراع التحكّم ومشغّل الكتف وحركاتك. تكون أحداث الحركة مصحوبةً برمز إجراء ومجموعة من قيم المحور. يحدد رمز الإجراء تغيير الحالة الذي حدث مثل تحريك ذراع التحكم. تصف قيم المحور الموضع وتصف قيم خصائص الحركة لتحكُّم مادي معيّن، مثل AXIS_X أو AXIS_RTRIGGER يمكنك الحصول على رمز الإجراء من خلال استدعاء getAction() وقيمة المحور عن طريق يَتِمُّ الْآنَ الِاتِّصَالْ بِـ getAxisValue().

يركز هذا الدرس على كيفية التعامل مع المدخلات من أكثر أنواع عناصر التحكّم المادية (أزرار لوحة الألعاب ولوحات التحكّم أذرع التحكم) في شاشة لعبة من خلال تنفيذ ما سبق ذكره 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() للحصول على رقم تعريف المحور.

يتم الضغط على أزرار جهاز التحكم في معالجة الألعاب

يوضح الشكل 1 كيف يعين Android رموز المفاتيح وقيم المحاور إلى عناصر التحكم في معظم أذرع التحكم في الألعاب.

الشكل 1. ملف شخصي لوحدة تحكُّم عامة في الألعاب

تشير وسائل الشرح في الشكل إلى ما يلي:

تتضمّن رموز المفاتيح الشائعة التي يتم إنشاؤها عند الضغط على أزرار جهاز التحكّم في الألعاب ما يلي: BUTTON_A, BUTTON_B, BUTTON_SELECT, وBUTTON_START. لعبة ما تؤدي وحدات التحكّم أيضًا إلى تشغيل رمز المفتاح DPAD_CENTER عند الضغط على وسط الشريط المتقاطع على لوحة التحكّم. بإمكان اللعبة فحص رمز المفتاح من خلال طلب الرقم getKeyCode(). أو من استدعاءات الأحداث الرئيسية مثل onKeyDown(), وإذا كان الحدث ذا صلة بلعبتك، تعامل معه على أنه والحركة في لعبة. يسرد الجدول 1 إجراءات الألعاب المقترَحة الأكثر شيوعًا أزرار جهاز التحكم في الألعاب.

الجدول 1. الإجراءات المقترَحة في ما يتعلّق بالألعاب على جهاز التحكّم في الألعاب الأزرار.

لعبة حركة رمز مفتاح الزر
ابدأ اللعبة في القائمة الرئيسية، أو أوقِفها مؤقتًا أو أُعيد تشغيلها أثناءها. BUTTON_START*
عرض القائمة BUTTON_SELECT* وKEYCODE_MENU*
نفس سلوك التنقل في نظام Android رجوع الموصوف في تصميم التنقّل الدليل. KEYCODE_BACK
الرجوع إلى عنصر سابق في إحدى القوائم BUTTON_B
تأكيد الاختيار أو تنفيذ إجراء اللعبة الأساسي BUTTON_A و DPAD_CENTER

* يجب ألا تعتمد لعبتك على قائمة "البداية" أو "الاختيار" أو "القائمة". الأزرار

نصيحة: ننصحك بتوفير شاشة إعداد. في لعبتك للسماح للمستخدمين بتخصيص عمليات ربط ذراع التحكّم في الألعاب إجراءات اللعبة.

يوضح المقتطف التالي كيفية إلغاء من onKeyDown() إلى ربط BUTTON_A يتم الضغط على زر "DPAD_CENTER". من خلال إحدى المهام في اللعبة.

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 مفتاح رجوع تلقائيًا. إذا كان تطبيقك متوافقًا مع أنظمة التشغيل Android هذه: الإصدارات، فتأكد من معالجة "BUTTON_A" اللعبة الأساسية اتخاذ القرار. لتحديد حزمة تطوير البرامج (SDK) الحالية لنظام التشغيل Android على الجهاز، فراجع قيمة Build.VERSION.SDK_INT.

إدخال لوحة اتجاهات العملية

لوحة الاتجاهات الرباعية (D-pad) هي أداة تحكّم مادي شائعة في العديد من الألعاب. ووحدات التحكم. يشير نظام التشغيل Android إلى الضغط على مفتاحَي D-pad للأعلى وللأسفل AXIS_HAT_Y حدثًا بنطاق من -1.0 (لأعلى) إلى 1.0 (أسفل)، اضغط على D-pad 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.
            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). احرص على التعامل مع كل من وحدات التحكم في التعليمة البرمجية.
  • التعامل مع الضغطات على مشغّل الكتف (مع توفير إدخال بديل ). تم تثبيت بعض وحدات التحكم باليدين اليسرى واليمنى. والمشغلات. في حال توفّر هذه المشغِّلات، يُبلغ نظام Android عن الضغط على المشغّل الأيسر كحدث AXIS_LTRIGGER و الضغط على زر السهم المتّجه لليمين حدث AXIS_RTRIGGER. على Android 4.3 (المستوى 18 من واجهة برمجة التطبيقات)، وهي وحدة تحكم تُنتج تبلغ أيضًا AXIS_LTRIGGER عن قيمة متطابقة للمحور AXIS_BRAKE. تشير رسالة الأشكال البيانية ينطبق الشيء نفسه على AXIS_RTRIGGER AXIS_GAS يبلغ Android عن كل المشغل التناظري بقيمة تمت تسويتها من 0.0 (تم إصدارها) إلى 1.0 (ضغطة كاملة). لا تحتوي جميع وحدات التحكم على مشغلات، لذا ضع في اعتبارك السماح للّاعبين بتنفيذ تلك إجراءات اللعبة باستخدام أزرار أخرى.