处理控制器操作

在系统级别,Android 会将来自游戏控制器的输入事件代码报告为 Android 键码和轴值。在游戏中,您可以接收这些键码和轴值,并将它们转换为特定的游戏内操作。

当玩家以物理方式将一个游戏控制器连接到其 Android 设备或以无线方式将这两者配对时,系统会自动将该控制器检测为输入设备,并开始报告其输入事件。通过在处于活动状态的 Activity 或处于焦点的 View 中实现以下回调方法(您应针对 ActivityView 实现回调,但不要同时针对这两者实现回调),您的游戏可以接收报告的输入事件:

建议的方法是,捕获来自用户与之互动的特定 View 对象的事件。检查回调提供的以下对象,以获取有关收到的输入事件类型的信息:

KeyEvent
描述方向键和游戏手柄按钮事件的对象。按键事件带有一个键码,该键码会指示触发的具体按钮(例如,DPAD_DOWNBUTTON_A)。您可以通过调用 getKeyCode() 获取该键码,也可以通过按键事件回调(例如,onKeyDown())获取该键码。
MotionEvent
描述来自操纵杆和肩部扳机移动的输入的对象。动作事件带有一个操作代码和一组轴值。操作代码指定了发生的状态变化(例如,操纵杆被移动)。轴值描述了特定物理控件的位置及其他移动属性(例如,AXIS_XAXIS_RTRIGGER)。您可以通过调用 getAction() 获取操作代码,通过调用 getAxisValue() 获取轴值。

本课程重点介绍了如何通过实现上述 View 回调方法以及处理 KeyEventMotionEvent 对象,处理来自游戏屏幕中最常见的物理控件(游戏手柄按钮、方向键和操纵杆)的输入。

验证游戏控制器是否已连接

报告输入事件时,Android 不会区分来自非游戏控制器设备的事件和来自游戏控制器的事件。例如,触摸屏操作会生成一个表示触控面的 X 坐标的 AXIS_X 事件,但操纵杆会生成一个表示操纵杆的 X 轴位置的 AXIS_X 事件。如果您的游戏关注游戏控制器输入的处理,您首先应检查输入事件是否来自相关的来源类型。

如需验证某个已连接的输入设备是否是游戏控制器,请调用 getSources() 以获取该设备支持的输入来源类型的组合位字段。然后,您可以进行测试以了解是否设置了以下字段:

  • SOURCE_GAMEPAD 来源类型表示输入设备具有游戏手柄按钮(例如,BUTTON_A)。请注意,尽管大多数游戏手柄通常都具有方向控件,但从严格意义上来说,此来源类型并不指示游戏控制器是否具有方向键按钮。
  • SOURCE_DPAD 来源类型表示输入设备具有方向键按钮(例如,DPAD_UP)。
  • SOURCE_JOYSTICK 来源类型表示输入设备具有模拟控制摇杆(例如,记录沿 AXIS_XAXIS_Y 的移动的操纵杆)。

以下代码段显示了一种辅助程序方法,通过该方法,您可以检查连接的输入设备是否是游戏控制器。如果是,则该方法会检索游戏控制器的设备 ID。然后,您可以将每个设备 ID 与游戏中的一位玩家相关联,并分别处理每位已连接的玩家的游戏操作。要详细了解如何支持同时连接到同一台 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(API 级别 19)或更高版本中,您可以通过调用 hasKeys(int...) 来确定已连接的游戏控制器是否支持某个键码。
  • 在 Android 3.1(API 级别 12)或更高版本中,您可以找到已连接的游戏控制器支持的所有可用轴,具体方法为:首先调用 getMotionRanges()。然后,在返回的每个 InputDevice.MotionRange 对象上,调用 getAxis() 以获取其轴 ID。

处理游戏手柄按钮按下操作

图 1 展示了 Android 如何将键码和轴值映射到大多数游戏控制器上的物理控件。

图 1. 常规游戏控制器的剖面图。

图中的标注是指以下内容:

游戏手柄按钮按下操作生成的常见键码包括 BUTTON_ABUTTON_BBUTTON_SELECTBUTTON_START。当按下方向键交叉按钮的中心部位时,一些游戏控制器还会触发 DPAD_CENTER 键码。您的游戏可以通过调用 getKeyCode() 检查该键码,也可以通过按键事件回调(例如 onKeyDown())检查该键码,如果该键码表示与您的游戏相关的事件,请将其作为游戏操作进行处理。表 1 列出了建议为最常见的游戏手柄按钮使用的游戏操作。

表 1. 建议为游戏手柄按钮使用的游戏操作。

游戏操作 按钮键码
在主菜单中启动游戏,或在游戏过程中暂停/取消暂停 BUTTON_START*
显示菜单 BUTTON_SELECT*KEYCODE_MENU*
导航设计指南中所述的 Android“返回”导航行为相同。 KEYCODE_BACK
返回到菜单中的上一项 BUTTON_B
确认选择,或执行主要游戏操作 BUTTON_ADPAD_CENTER

* 您的游戏不应依赖于“开始”、“选择”或“菜单”按钮的存在。

提示:考虑在游戏中提供配置屏幕,让用户可以针对游戏操作设置个性化的游戏控制器映射方式。

以下代码段展示了如何替换 onKeyDown(),以将 BUTTON_ADPAD_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(API 级别 17)及更低版本中,系统默认将 BUTTON_A 视为 Android 的“返回”键。如果您的应用支持这些 Android 版本,请务必将 BUTTON_A 作为主要游戏操作。如需确定设备上当前使用的 Android SDK 版本,请参阅 Build.VERSION.SDK_INT 值。

处理方向键输入

四向方向键是许多游戏控制器中常用的物理控件。Android 将方向键“上”和“下”按下操作报告为 AXIS_HAT_Y 事件,范围为 -1.0(上)至 1.0(下);将方向键“左”或“右”按下操作报告为 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(API 级别 18)上,产生 AXIS_LTRIGGER 的控制器也会针对 AXIS_BRAKE 轴报告完全相同的值。AXIS_RTRIGGERAXIS_GAS 也是如此。Android 会使用介于 0.0(已松开)和 1.0(完全按下)之间的标准化值报告所有模拟扳机按下操作。并非所有控制器都有扳机,因此请考虑允许玩家使用其他按钮来执行这些游戏操作。