Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

컨트롤러 작업 처리

시스템 레벨에서 Android는 게임 컨트롤러의 입력 이벤트 코드를 Android 키 코드와 축 값으로 보고합니다. 게임에서 이러한 코드와 값을 받아 특정 인게임 작업으로 변환할 수 있습니다.

플레이어가 게임 컨트롤러를 Android 지원 기기에 물리적으로 연결하거나 무선으로 페어링하면 시스템은 컨트롤러를 입력 기기로 자동 감지하고 입력 이벤트를 보고하기 시작합니다. 게임은 활성 상태의 Activity 또는 포커스 View에 다음 콜백 메서드를 구현하여 이러한 입력 이벤트를 받을 수 있습니다. 콜백은 Activity 또는 View 중 한 곳에만 구현해야 합니다.

사용자가 상호작용하는 특정 View 객체의 이벤트를 캡처하는 접근 방식을 추천합니다. 콜백에서 제공한 다음 객체를 검사하여 수신한 입력 이벤트의 유형에 관한 정보를 가져옵니다.

KeyEvent
방향 패드(D패드) 및 게임패드의 버튼 이벤트를 나타내는 객체입니다. 키 이벤트는 트리거된 특정 버튼을 나타내는 키 코드(예: DPAD_DOWN 또는 BUTTON_A)를 수반합니다. getKeyCode()를 호출하거나 onKeyDown() 같은 키 이벤트 콜백을 통해 키 코드를 가져올 수 있습니다.
MotionEvent
조이스틱 및 숄더 트리거 동작의 입력을 나타내는 객체입니다. 모션 이벤트는 작업 코드와 축 값의 집합을 수반합니다. 작업 코드는 조이스틱이 이동하는 것과 같은 상태 변경을 지정합니다. 축 값은 특정 물리적 컨트롤의 위치 및 기타 이동 속성(예: AXIS_X 또는 AXIS_RTRIGGER)을 설명합니다. 작업 코드를 가져오려면 getAction()을 호출하고 축 값을 가져오려면 getAxisValue()를 호출합니다.

이 과정에서는 위에서 언급한 View 콜백 메서드를 구현하고 KeyEventMotionEvent 객체를 처리하여 게임 화면에서 가장 공통적인 유형의 물리적 컨트롤(게임패드 버튼, 방향 패드 및 조이스틱)에서 받은 입력을 처리하는 방법을 중점적으로 설명합니다.

게임 컨트롤러가 연결되어 있는지 확인

입력 이벤트를 보고할 때 Android는 게임 컨트롤러가 아닌 기기에서 발생한 이벤트와 게임 컨트롤러에서 발생한 이벤트를 구별하지 않습니다. 예를 들어, 터치 스크린 작업에서는 터치 스크린의 X 좌표를 나타내는 AXIS_X 이벤트가 발생하지만, 조이스틱에서는 조이스틱의 X 좌표를 나타내는 AXIS_X 이벤트가 발생합니다. 게임에서 게임 컨트롤러 입력을 처리하려고 하면 먼저 입력 이벤트가 관련된 소스 유형에서 발생하는지 확인해야 합니다.

연결된 입력 기기가 게임 컨트롤러인지 확인하려면 getSources()를 호출하여 기기에서 지원하는 입력 소스 유형의 결합된 비트 필드를 가져옵니다. 그런 다음 아래 필드가 설정되어 있는지 확인하기 위해 테스트할 수 있습니다.

  • SOURCE_GAMEPAD의 소스 유형은 입력 기기에 게임패드 버튼(예: BUTTON_A)이 있음을 나타냅니다. 이 소스 유형은 게임 컨트롤러에 D패드 버튼이 있는지 확실히 표시하지 않습니다. 다만 대부분의 게임패드에는 일반적으로 방향 컨트롤이 있습니다.
  • SOURCE_DPAD의 소스 유형은 입력 기기에 D패드 버튼(예: 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
    }
    

자바

    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_A, BUTTON_B, BUTTON_SELECTBUTTON_START를 포함합니다. 또한, 일부 게임 컨트롤러는 D패드 크로스바의 중앙이 눌렸을 때 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
    }
    

자바

    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 값을 참조하세요.

방향 패드 입력 처리

4방향 패드(D패드)는 많은 게임 컨트롤러에서 일반적인 물리적 컨트롤입니다. Android는 D패드의 위/아래가 눌리면 -1.0(위)에서 1.0(아래) 사이의 값을 가진 AXIS_HAT_Y 이벤트로 보고하고 D패드의 왼쪽/오른쪽이 눌리면 -1.0(왼쪽)에서 1.0(오른쪽) 사이의 값을 가진 AXIS_HAT_X 이벤트로 보고합니다.

일부 컨트롤러는 D패드가 눌렸을 때 이벤트 대신 키 코드로 보고합니다. 게임에서 D패드 눌림을 감지해야 한다면 표 2에서 권장하는 것과 같이 해트 축 이벤트와 D패드 키 코드를 동일한 입력 이벤트로 취급해야 합니다.

표 2. 기본 게임 작업에 권장하는 D패드 키 코드 및 해트 축 값

게임 작업 D패드 키 코드 해트 축 코드
위로 이동 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의 값을 가짐)

다음 코드 스니펫은 입력 이벤트에서 해트 축 및 키 코드 값을 확인하여 D패드의 방향을 결정하도록 도와주는 클래스 사용법을 보여줍니다.

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

자바

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

게임에서 D패드 입력을 처리할 때(예: 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.
        ...
    }
    

자바

    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는 ACTION_MOVE 작업 코드와 조이스틱 축의 업데이트된 위치를 포함하는 MotionEvent를 보고합니다. 게임은 MotionEvent에서 제공하는 데이터를 사용하여 게임에 의미있는 조이스틱 이동이 발생했는지 확인할 수 있습니다.

조이스틱 모션 이벤트는 단일 개체 내에서 여러 이동 샘플을 함께 일괄 처리할 수 있습니다. MotionEvent 개체는 각 축의 여러 이전 위치와 함께 각 조이스틱 축의 현재 위치를 포함합니다. Android는 ACTION_MOVE 작업 코드를 가진 모션 이벤트(예: 조이스틱 이동)를 보고할 때 효율성을 위해 축 값을 일괄 처리합니다. 축의 이전 값은 현재 축 값보다 오래된 별개의 값 집합 및 이전 모션 이벤트에서 보고된 값보다 더 최근의 값으로 구성됩니다. 자세한 내용은 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)
            }
        }
    }
    

자바

    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
    }
    

자바

    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
    }
    

자바

    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 이벤트로 보고합니다. 오른쪽 스틱의 경우 수평 이동은 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(완전히 눌림)으로 완전히 정규화된 값을 보고합니다. 일부 컨트롤러에는 트리거가 없으므로 플레이어가 다른 버튼으로 게임 작업을 할 수 있게 하는 것이 좋습니다.