Menangani tindakan pengontrol

Pada tingkat sistem, Android melaporkan kode peristiwa input dari pengontrol game sebagai kode tombol Android dan nilai sumbu. Dalam game, Anda dapat menerima kode dan nilai ini, kemudian mengonversinya menjadi tindakan spesifik dalam game.

Saat pemain secara fisik atau nirkabel menyambungkan pengontrol game ke perangkat Android mereka, sistem akan secara otomatis mendeteksi pengontrol tersebut sebagai perangkat input dan mulai melaporkan peristiwa inputnya. Game Anda dapat menerima peristiwa input ini dengan mengimplementasikan metode callback berikut di Activity aktif Anda atau View yang difokuskan (Anda perlu mengimplementasikan callback untuk Activity atau View, tetapi tidak keduanya):

Pendekatan yang direkomendasikan adalah untuk mencatat peristiwa dari objek View tertentu tempat pengguna berinteraksi. Periksa objek berikut yang diberikan oleh callback untuk mendapatkan informasi terkait jenis peristiwa input yang diterima:

KeyEvent
Objek yang mendeskripsikan peristiwa tombol arah (D-pad) dan tombol gamepad. Peristiwa tombol disertai dengan kode tombol yang menunjukkan bahwa tombol tertentu telah dipicu, seperti DPAD_DOWN atau BUTTON_A. Anda bisa mendapatkan kode tombol dengan memanggil getKeyCode() atau dari callback peristiwa tombol seperti onKeyDown().
MotionEvent
Objek yang mendeskripsikan input dari joystick dan pergerakan shoulder trigger. Peristiwa gerakan disertai dengan kode tindakan dan serangkaian nilai sumbu. Kode tindakan menentukan perubahan status yang terjadi, seperti joystick digerakkan. Nilai sumbu mendeskripsikan posisi dan properti gerakan lain untuk kontrol fisik tertentu, seperti AXIS_X atau AXIS_RTRIGGER. Anda bisa mendapatkan kode tindakan dengan memanggil getAction() dan nilai sumbu dengan memanggil getAxisValue().

Tutorial ini berfokus pada cara Anda dapat menangani input dari jenis kontrol fisik yang paling umum (tombol gamepad, tombol arah, dan joystick) dalam layar game dengan mengimplementasikan metode callback View yang disebutkan di atas dan memproses objek KeyEvent dan MotionEvent.

Memverifikasi bahwa pengontrol game telah tersambung

Saat melaporkan peristiwa input, Android tidak membedakan antara peristiwa yang berasal dari perangkat yang bukan pengontrol game dan peristiwa yang berasal dari pengontrol game. Misalnya, tindakan penyentuhan layar menghasilkan peristiwa AXIS_X yang mewakili koordinat X dari menu kontrol, tetapi joystick menghasilkan peristiwa AXIS_X yang mewakili posisi X joystick. Jika game Anda peduli dengan penanganan input pengontrol game, pertama Anda harus memastikan bahwa peristiwa input berasal dari jenis sumber yang relevan.

Untuk memverifikasi bahwa perangkat input yang tersambung adalah pengontrol game, panggil getSources() untuk mendapatkan kolom bit gabungan dari jenis sumber input yang didukung di perangkat tersebut. Kemudian Anda dapat menguji apakah kolom berikut telah disetel:

  • Jenis sumber SOURCE_GAMEPAD menunjukkan bahwa perangkat input memiliki tombol gamepad (misalnya, BUTTON_A). Perlu diperhatikan bahwa jenis sumber ini tidak hanya menunjukkan jika pengontrol game memiliki tombol D-pad, meskipun sebagian besar gamepad biasanya memiliki tombol arah.
  • Jenis sumber SOURCE_DPAD menunjukkan bahwa perangkat input memiliki tombol D-pad (misalnya, DPAD_UP).
  • Jenis sumber SOURCE_JOYSTICK menunjukkan bahwa perangkat input memiliki stick kontrol analog (misalnya, joystick yang mencatat pergerakan sepanjang AXIS_X dan AXIS_Y).

Cuplikan kode berikut menunjukkan metode helper yang memungkinkan Anda memeriksa apakah perangkat input yang tersambung merupakan pengontrol game. Jika ya, metode tersebut mengambil ID perangkat untuk pengontrol game. Kemudian Anda dapat mengaitkan setiap ID perangkat dengan pemain di game Anda, dan memproses tindakan game untuk setiap pemain yang tersambung secara terpisah. Untuk mempelajari lebih lanjut tentang mendukung beberapa pengontrol game yang secara bersamaan tersambung ke perangkat Android yang sama, buka Mendukung beberapa pengontrol game.

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

Selain itu, sebaiknya periksa setiap kemampuan input yang didukung oleh pengontrol game tersambung. Ini mungkin akan berguna, misalnya, jika Anda ingin agar game hanya menggunakan input dari serangkaian kontrol fisik yang dipahaminya.

Untuk mendeteksi apakah kode tombol atau kode sumbu tertentu didukung oleh pengontrol game yang tersambung, gunakan teknik ini:

  • Di Android versi 4.4 (API level 19) atau yang lebih tinggi, Anda dapat menentukan apakah suatu kode tombol didukung di pengontrol game yang tersambung dengan memanggil hasKeys(int...).
  • Di Android versi 3.1 (API level 12) atau yang lebih tinggi, Anda dapat menemukan semua sumbu tersedia yang didukung oleh pengontrol game tersambung dengan memanggil getMotionRanges() terlebih dahulu. Kemudian, pada setiap objek InputDevice.MotionRange yang ditampilkan, panggil getAxis() untuk mendapatkan ID sumbunya.

Memproses penekanan pada tombol gamepad

Gambar 1 menunjukkan bagaimana Android memetakan kode tombol dan nilai sumbu ke kontrol fisik di sebagian besar pengontrol game.

Gambar 1. Profil untuk pengontrol game umum.

Info dalam gambar tersebut merujuk pada yang berikut ini:

Kode tombol umum yang dihasilkan oleh penekanan tombol gamepad mencakup BUTTON_A, BUTTON_B, BUTTON_SELECT, dan BUTTON_START. Beberapa pengontrol game juga memicu kode tombol DPAD_CENTER saat bagian tengah crossbar D-pad ditekan. Game Anda dapat memeriksa kode kunci dengan memanggil getKeyCode() atau dari callback peristiwa tombol seperti onKeyDown(), dan jika kode tombol mewakili peristiwa yang relevan bagi game Anda, game akan memprosesnya sebagai tindakan game. Tabel 1 mencantumkan tindakan game yang direkomendasikan untuk tombol gamepad yang paling umum.

Tabel 1. Tindakan game yang direkomendasikan untuk tombol gamepad.

Tindakan Game Kode Tombol
Memulai game di menu utama, atau menjeda/melanjutkan selama game BUTTON_START*
Menu tampilan BUTTON_SELECT* dan KEYCODE_MENU*
Sama seperti perilaku navigasi Back Android yang dijelaskan di panduan desain Navigation. KEYCODE_BACK
Kembali ke item sebelumnya di menu BUTTON_B
Mengonfirmasi pilihan, atau melakukan tindakan game utama BUTTON_A dan DPAD_CENTER

* Game Anda tidak boleh bergantung pada keberadaan tombol Start, Select, atau Menu.

Tips: Pertimbangkan untuk memberikan layar konfigurasi di game Anda guna memungkinkan pengguna mempersonalisasi pemetaan pengontrol game mereka untuk tindakan game.

Cuplikan berikut menunjukkan cara menggantikan onKeyDown() untuk mengaitkan penekanan tombol BUTTON_A dan DPAD_CENTER dengan tindakan game.

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

Catatan: Di Android versi 4.2 (API level 17) dan yang lebih rendah, sistem memperlakukan BUTTON_A sebagai tombol Back Android secara default. Jika aplikasi Anda mendukung versi Android ini, pastikan untuk memperlakukan BUTTON_A sebagai tindakan game utama. Untuk menentukan versi Android SDK saat ini di perangkat, lihat nilai Build.VERSION.SDK_INT.

Memproses input tombol arah

Tombol 4 arah (D-pad) merupakan kontrol fisik yang umum di banyak pengontrol game. Android melaporkan penekanan D-pad ATAS dan BAWAH sebagai peristiwa AXIS_HAT_Y dengan rentang dari -1,0 (atas) hingga 1,0 (bawah), dan penekanan D-pad KIRI atau KANAN sebagai peristiwa AXIS_HAT_X dengan rentang dari -1,0 (kiri) hingga 1,0 (kanan).

Namun, beberapa pengontrol melaporkan penekanan D-pad dengan kode tombol. Jika game Anda peduli dengan penekanan D-pad, Anda harus memperlakukan peristiwa sumbu hat dan kode tombol D-pad sebagai peristiwa input yang sama, seperti yang direkomendasikan di tabel 2.

Tabel 2. Tindakan game default yang direkomendasikan untuk kode tombol D-pad dan nilai sumbu hat.

Tindakan Game Kode Tombol D-pad Kode Sumbu Hat
Naikkan KEYCODE_DPAD_UP AXIS_HAT_Y (untuk nilai 0 hingga -1,0)
Turunkan KEYCODE_DPAD_DOWN AXIS_HAT_Y (untuk nilai 0 hingga 1,0)
Pindahkan ke Kiri KEYCODE_DPAD_LEFT AXIS_HAT_X (untuk nilai 0 hingga -1,0)
Pindahkan ke Kanan KEYCODE_DPAD_RIGHT AXIS_HAT_X (untuk nilai 0 hingga 1,0)

Cuplikan kode berikut menunjukkan class helper yang memungkinkan Anda memeriksa nilai kode tombol dan sumbu hat dari peristiwa input untuk menentukan arah D-pad.

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

Anda dapat menggunakan class helper ini di game Anda kapan pun Anda ingin memproses input D-pad (misalnya, di callback onGenericMotionEvent() atau onKeyDown()).

Contoh:

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.
        ...
    }
    

Memproses pergerakan joystick

Saat pemain menggerakkan joystick di pengontrol game mereka, Android melaporkan MotionEvent yang berisi kode tindakan ACTION_MOVE dan posisi sumbu joystick yang diperbarui. Game Anda dapat menggunakan data yang diberikan oleh MotionEvent untuk menentukan apakah pergerakan joystick yang dipedulikan terjadi.

Perlu diperhatikan bahwa peristiwa gerakan joystick mungkin menumpuk beberapa contoh pergerakan bersama dengan satu objek. Objek MotionEvent berisi posisi saat ini untuk setiap sumbu joystick serta beberapa posisi historis untuk setiap sumbu. Saat melaporkan peristiwa motion dengan kode tindakan ACTION_MOVE (seperti pergerakan joystick), Android akan menumpuk nilai sumbu demi efisiensi. Nilai historis untuk sumbu mencakup serangkaian nilai yang berbeda yang lebih lama dari nilai sumbu saat ini, dan lebih baru dari nilai yang dilaporkan di semua peristiwa gerakan sebelumnya. Buka referensi MotionEvent untuk mengetahui detailnya.

Anda dapat menggunakan informasi historis untuk merender pergerakan objek game berdasarkan input joystick secara lebih akurat. Untuk mengambil nilai saat ini dan nilai historis, panggil getAxisValue() atau getHistoricalAxisValue(). Anda juga dapat menemukan jumlah titik historis di peristiwa joystick dengan memanggil getHistorySize().

Cuplikan berikut menunjukkan cara menggantikan callback onGenericMotionEvent() untuk memproses input joystick. Pertama Anda harus memproses nilai historis untuk sumbu, kemudian memproses posisinya saat ini.

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

Sebelum menggunakan input joystick, Anda perlu menentukan apakah joystick terpusat, kemudian menghitung pergerakan sumbunya berdasarkan informasi tersebut. Joystick biasanya memiliki area flat, yaitu, rentang nilai yang dekat dengan koordinat (0,0) yang pada posisi tersebut sumbu dianggap terpusat. Jika nilai sumbu yang dilaporkan Android berada dalam area flat, Anda harus memperlakukan pengontrol agar diistirahatkan (yaitu, tidak bergerak di sepanjang kedua sumbu).

Cuplikan di bawah menunjukkan metode helper yang menghitung pergerakan di sepanjang setiap sumbu. Anda memanggil helper ini di metode processJoystickInput() yang dijelaskan secara lebih lanjut di bawah.

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

Dengan menggabungkan semua informasi di atas, berikut cara Anda dapat memproses pergerakan joystick dalam game:

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
    }
    

Untuk mendukung pengontrol game dengan fitur yang lebih canggih dari sekadar satu joystick, ikuti praktik terbaik berikut:

  • Menangani stick pengontrol ganda. Sebagian besar pengontrol game memiliki joystick kiri dan kanan. Untuk stick sebelah kanan, Android melaporkan pergerakan horizontal sebagai peristiwa AXIS_X dan pergerakan vertikal sebagai peristiwa AXIS_Y. Untuk stick sebelah kanan, Android melaporkan pergerakan horizontal sebagai peristiwa AXIS_Z dan pergerakan vertikal sebagai peristiwa AXIS_RZ. Pastikan untuk menangani kedua stick pengontrol di kode Anda.
  • Menangani penekanan shoulder trigger (tetapi memberikan metode masukan alternatif). Beberapa pengontrol memiliki shoulder trigger kiri dan kanan. Jika trigger tersebut ada, Android melaporkan penekanan trigger sebelah kiri sebagai peristiwa AXIS_LTRIGGER dan penekanan trigger sebelah kanan sebagai peristiwa AXIS_RTRIGGER. Pada Android versi 4.3 (API level 18), pengontrol yang menghasilkan AXIS_LTRIGGER juga melaporkan nilai yang identik untuk sumbu AXIS_BRAKE. Hal yang sama juga berlaku untuk AXIS_RTRIGGER dan AXIS_GAS. Android melaporkan semua penekanan trigger analog dengan nilai yang dinormalkan dari 0,0 (dilepaskan) to 1,0 (ditekan sepenuhnya). Tidak semua pengontrol memiliki trigger, jadi pertimbangkan untuk mengizinkan pemain melakukan tindakan game tersebut dengan tombol lain.