Menangani tindakan pengontrol

Di tingkat sistem, Android melaporkan kode peristiwa input dari pengontrol game sebagai kode tombol Android dan nilai sumbu. Di game Anda, Anda dapat menerima kode ini dan nilai serta mengubahnya menjadi tindakan spesifik dalam game.

Saat pemain terhubung secara fisik atau menyambungkan secara nirkabel pengontrol game untuk perangkat berbasis Android, sistem secara otomatis mendeteksi pengontrol sebagai perangkat input dan mulai melaporkan peristiwa inputnya. Game Anda dapat menerima peristiwa input ini dengan menerapkan metode callback berikut di kolom Activity atau View terfokus (Anda harus menerapkan callback untuk Activity atau View, tetapi tidak keduanya):

Pendekatan yang direkomendasikan adalah menangkap peristiwa dari objek View tertentu yang berinteraksi dengan pengguna. Periksa objek berikut yang disediakan oleh callback untuk mendapatkan informasi tentang jenis kejadian input yang diterima:

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

Pelajaran ini berfokus pada bagaimana Anda dapat menangani {i>input<i} dari jenis kontrol fisik (tombol gamepad, tombol arah, dan joystick) di layar game dengan menerapkan hal yang disebutkan di atas Metode dan pemrosesan callback View Objek KeyEvent dan MotionEvent.

Memverifikasi bahwa pengontrol game telah tersambung

Saat melaporkan peristiwa input, Android tidak membedakan antara peristiwa yang berasal dari perangkat non-pengontrol game dan peristiwa yang datang dari pengontrol game. Misalnya, tindakan layar sentuh menghasilkan Peristiwa AXIS_X yang merepresentasikan X koordinat permukaan sentuh, tetapi joystick menghasilkan Peristiwa AXIS_X yang merepresentasikan posisi X joystick. Jika game Anda peduli dengan penanganan input pengontrol game, Anda harus terlebih dahulu memeriksa bahwa peristiwa input berasal dari jenis sumber yang relevan.

Untuk memverifikasi bahwa perangkat input yang terhubung adalah pengontrol game, panggil getSources() untuk mendapatkan isian bit gabungan dari jenis sumber input yang didukung pada perangkat tersebut. Anda kemudian dapat menguji untuk melihat apakah kolom berikut ditetapkan:

  • Jenis sumber SOURCE_GAMEPAD menunjukkan bahwa perangkat input memiliki tombol gamepad (misalnya, BUTTON_A). Perhatikan bahwa sumber ini tidak hanya menunjukkan jika pengontrol game memiliki tombol D-pad, meskipun sebagian besar gamepad biasanya memiliki kontrol 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 stik kontrol analog (misalnya, joystick yang merekam pergerakan di sepanjang AXIS_X dan AXIS_Y).

Cuplikan kode berikut menunjukkan metode bantuan yang memungkinkan Anda memeriksa apakah perangkat input yang terhubung adalah pengontrol {i>game<i}. Jika ya, metode akan mengambil ID perangkat untuk pengontrol game. Anda lalu dapat menghubungkan setiap perangkat ID dengan pemain di game Anda, dan proses tindakan game untuk setiap koneksi pemutar video secara terpisah. Untuk mempelajari lebih lanjut cara mendukung beberapa pengontrol game yang terhubung secara bersamaan di perangkat Android yang sama, lihat 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, Anda mungkin ingin memeriksa kemampuan input didukung oleh pengontrol game yang terhubung. Hal ini mungkin bermanfaat, misalnya jika Anda ingin game hanya menggunakan input dari rangkaian kontrol fisik. dimengerti.

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

  • Di Android 4.4 (API level 19) atau yang lebih tinggi, Anda bisa menentukan apakah kode kunci didukung pada pengontrol game yang terhubung dengan memanggil hasKeys(int...).
  • Di Android 3.1 (API level 12) atau yang lebih tinggi, Anda dapat menemukan semua sumbu yang tersedia didukung pada pengontrol game yang terhubung dengan memanggil getMotionRanges(). Kemudian, pada setiap Objek InputDevice.MotionRange ditampilkan, panggil getAxis() untuk mendapatkan ID sumbunya.

Memproses penekanan pada tombol gamepad

Gambar 1 menunjukkan cara Android memetakan kode tombol dan nilai sumbu ke elemen pengontrol pada 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 pertandingan pengontrol juga memicu kode tombol DPAD_CENTER saat bagian tengah crossbar D-pad ditekan. Nama game dapat memeriksa kode tombol dengan memanggil getKeyCode() atau dari callback peristiwa utama seperti onKeyDown(), dan jika mewakili peristiwa yang relevan dengan game Anda, proses sebagai game aksi. Tabel 1 mencantumkan tindakan game yang direkomendasikan untuk tombol gamepad.

Tabel 1. Tindakan game yang direkomendasikan untuk gamepad tombol.

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 Desain Navigasi kami. 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 Mulai, Pilih, atau Menu tombol.

Tips: Pertimbangkan untuk menyediakan layar konfigurasi dalam game agar pengguna dapat mempersonalisasi pemetaan pengontrol game mereka sendiri untuk dalam game mereka.

Cuplikan berikut menunjukkan cara mengganti onKeyDown() hingga hubungkan BUTTON_A dan DPAD_CENTER penekanan tombol dengan aksi 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 4.2 (API level 17) dan yang lebih rendah, sistem akan memperlakukan BUTTON_A sebagai Android Tombol Kembali secara default. Jika aplikasi Anda mendukung Android ini versi, pastikan untuk memperlakukan BUTTON_A sebagai pertandingan utama tindakan. Untuk menentukan Android SDK saat ini di perangkat, lihat Nilai Build.VERSION.SDK_INT.

Memproses input tombol arah

4-way directional pad (D-pad) adalah kontrol fisik yang umum dalam banyak game Domain Controllers. Android melaporkan penekanan D-pad ATAS dan BAWAH sebagai AXIS_HAT_Y peristiwa dengan rentang dari -1.0 (atas) hingga 1.0 (bawah), dan D-pad LEFT atau RIGHT menekan sebagai AXIS_HAT_X peristiwa dengan rentang dari -1.0 (kiri) hingga 1,0 (kanan).

Namun, beberapa pengontrol melaporkan penekanan D-pad dengan kode tombol. Jika game Anda peduli tentang penekanan D-pad, Anda harus menangani kejadian sumbu topi dan tombol D-pad kode tombol sebagai peristiwa input yang sama, seperti yang direkomendasikan dalam tabel 2.

Tabel 2. Tindakan game default yang direkomendasikan untuk tombol D-pad kode 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 topi nilai sumbu dan kode tombol 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 bantuan ini di game Anda di mana pun Anda ingin memproses Input D-pad (misalnya, dalam 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, Android melaporkan MotionEvent yang berisi Kode tindakan ACTION_MOVE dan yang diperbarui posisi sumbu joystick. Game Anda dapat menggunakan data yang disediakan oleh MotionEvent untuk menentukan apakah joystick menggerakkannya terjadi.

Perhatikan bahwa peristiwa gerakan joystick dapat mengelompokkan beberapa contoh gerakan secara bersamaan dalam satu objek. Objek MotionEvent berisi posisi saat ini untuk setiap sumbu joystick serta beberapa posisi untuk setiap sumbu. Saat melaporkan peristiwa gerakan dengan kode tindakan ACTION_MOVE (seperti pergerakan joystick), Android akan mengelompokkan nilai sumbu untuk efisiensi. Nilai historis untuk sumbu terdiri dari kumpulan nilai berbeda yang lebih lama dari nilai sumbu saat ini, dan lebih baru dari nilai yang dilaporkan dalam setiap peristiwa gerakan sebelumnya. Lihat Referensi MotionEvent untuk mengetahui detailnya.

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

Cuplikan berikut menunjukkan cara mengganti Callback onGenericMotionEvent() untuk memproses input joystick. Anda harus terlebih dahulu memproses nilai historis untuk sumbu, lalu 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 berada di tengah, kemudian hitung gerakan sumbunya. Joystick biasanya memiliki area datar, yaitu rentang nilai yang dekat dengan koordinat (0,0) di mana sumbu dianggap terpusat. Jika nilai sumbu yang dilaporkan oleh Android berada dalam area datar, Anda harus memperlakukan pengontrol sebagai istirahat (yaitu, tidak bergerak di sepanjang kedua sumbu).

Cuplikan di bawah ini menunjukkan metode bantuan yang menghitung pergerakan sepanjang setiap sumbu. Anda memanggil helper ini dalam metode processJoystickInput() dijelaskan 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;
}

Menyatukan semuanya, berikut cara memproses pergerakan joystick di game Anda:

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 yang memiliki fitur fitur lebih dari satu joystick, ikuti praktik terbaik berikut:

  • Menangani stick pengontrol ganda. Banyak pengontrol {i>game<i} memiliki joystick kiri dan kanan. Untuk batang kiri, Android melaporkan gerakan horizontal sebagai peristiwa AXIS_X dan gerakan vertikal sebagai peristiwa AXIS_Y. Untuk stik kanan, Android melaporkan gerakan horizontal sebagai Peristiwa AXIS_Z dan pergerakan vertikal sebagai AXIS_RZ peristiwa. Pastikan untuk menangani kedua {i>controller<i} menempel pada kode Anda.
  • Menangani penekanan shoulder trigger (tetapi memberikan input alternatif metode). Beberapa pengontrol memiliki bahu kiri dan kanan pemicu. Jika pemicu ini ada, Android akan melaporkan penekanan pemicu kiri sebagai peristiwa AXIS_LTRIGGER dan penekanan pemicu kanan sebagai Peristiwa AXIS_RTRIGGER. Di Android 4.3 (API level 18), pengontrol yang menghasilkan AXIS_LTRIGGER juga melaporkan nilai yang sama untuk sumbu AXIS_BRAKE. Tujuan hal yang sama berlaku untuk AXIS_RTRIGGER dan AXIS_GAS. Android melaporkan semua pemicu analog menekan dengan nilai yang dinormalkan dari 0,0 (dilepaskan) ke 1,0 (ditekan sepenuhnya). Bukan semua pengontrol memiliki {i>trigger<i}, jadi pertimbangkan untuk mengizinkan pemain melakukan tindakan game dengan tombol lain.