여러 게임 컨트롤러 지원

대부분의 게임은 Android 기기당 한 명의 사용자를 지원하도록 설계하지만 동일한 Android 기기에 동시에 연결된 게임 컨트롤러로 여러 사용자를 지원할 수도 있습니다.

이 강의에서는 연결된 여러 컨트롤러에서 단일 기기 멀티 플레이어 게임의 입력을 처리하는 몇 가지 기본 기술을 다룹니다. 여기에는 플레이어 아바타와 각 컨트롤러 기기 간의 매핑을 유지하고 컨트롤러 입력 이벤트를 적절하게 처리하는 작업이 포함됩니다.

플레이어를 컨트롤러 기기 ID에 매핑

게임 컨트롤러가 Android 기기에 연결되면 시스템은 정수로 된 기기 ID를 할당합니다. 게임 컨트롤러가 연결되었는지 확인에서 보는 것처럼 InputDevice.getDeviceIds()를 호출하여 연결된 게임 컨트롤러의 기기 ID를 얻을 수 있습니다. 그런 다음 각 기기 ID를 게임 플레이어와 연결하고 각 플레이어의 게임 작업을 개별적으로 처리하면 됩니다.

참고: Android 4.1(API 레벨 16) 이상을 실행하는 기기에서는 getDescriptor()를 사용하여 입력 기기의 설명어를 가져올 수 있으며, 설명어는 입력 기기 고유의 변경되지 않는 문자열 값입니다. 기기 ID와 달리 입력 기기가 연결 해제, 다시 연결 또는 재구성된 경우에도 설명어 값은 변하지 않습니다.

아래 코드 스니펫은 SparseArray를 사용하여 플레이어의 아바타를 특정 컨트롤러와 연결하는 방법을 보여줍니다. 이 예에서 mShips 변수는 Ship 개체 모음을 저장합니다. 새 플레이어 아바타는 사용자가 새 컨트롤러를 연결하면 게임 내에서 생성되고 연결된 컨트롤러가 삭제되면 아바타도 없어집니다.

onInputDeviceAdded()onInputDeviceRemoved() 콜백 메서드는 다양한 버전의 Android에서 컨트롤러 지원에 소개된 추상화 레이어의 일부입니다. 이러한 리스너 콜백을 구현하면 컨트롤러가 추가되거나 삭제될 때 게임에서 게임 컨트롤러의 기기 ID를 식별할 수 있습니다. 이 감지 기능은 Android 2.3(API 레벨 9) 이상에서 사용할 수 있습니다.

Kotlin

    private val ships = SparseArray<Ship>()

    override fun onInputDeviceAdded(deviceId: Int) {
        getShipForID(deviceId)
    }

    override fun onInputDeviceRemoved(deviceId: Int) {
        removeShipForID(deviceId)
    }

    private fun getShipForID(shipID: Int): Ship {
        return ships.get(shipID) ?: Ship().also {
            ships.append(shipID, it)
        }
    }

    private fun removeShipForID(shipID: Int) {
        ships.remove(shipID)
    }
    

자바

    private final SparseArray<Ship> ships = new SparseArray<Ship>();

    @Override
    public void onInputDeviceAdded(int deviceId) {
        getShipForID(deviceId);
    }

    @Override
    public void onInputDeviceRemoved(int deviceId) {
        removeShipForID(deviceId);
    }

    private Ship getShipForID(int shipID) {
        Ship currentShip = ships.get(shipID);
        if ( null == currentShip ) {
            currentShip = new Ship();
            ships.append(shipID, currentShip);
        }
        return currentShip;
    }

    private void removeShipForID(int shipID) {
        ships.remove(shipID);
    }
    

여러 컨트롤러 입력 처리

게임에서 여러 컨트롤러의 입력을 처리하려면 다음 루프를 실행해야 합니다.

  1. 입력 이벤트가 발생했는지 감지합니다.
  2. 입력 소스와 기기 ID를 확인합니다.
  3. 입력 이벤트 키 코드 또는 축 값이 나타내는 작업에 따라 기기 ID와 연결된 플레이어 아바타를 업데이트합니다.
  4. 사용자 인터페이스를 렌더링하고 업데이트합니다.

KeyEventMotionEvent 입력 이벤트에는 이벤트와 연결된 기기 ID가 있습니다. 게임은 이를 이용하여 게임에서 입력 이벤트가 발생한 컨트롤러를 판별하고 컨트롤러와 연결된 플레이어 아바타를 업데이트할 수 있습니다.

다음 코드 스니펫은 게임 컨트롤러의 기기 ID에 대응하는 플레이어 아바타 참조를 가져오고 컨트롤러에서 사용자의 버튼을 눌러 게임을 업데이트하는 방법을 보여줍니다.

Kotlin

    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        if (event.source and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD) {
            event.deviceId.takeIf { it != -1 }?.also { deviceId ->
                val currentShip: Ship = getShipForID(deviceId)
                // Based on which key was pressed, update the player avatar
                // (e.g. set the ship headings or fire lasers)
                return true
            }
        }
        return super.onKeyDown(keyCode, event)
    }
    

자바

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)
                    == InputDevice.SOURCE_GAMEPAD) {
            int deviceId = event.getDeviceId();
            if (deviceId != -1) {
                Ship currentShip = getShipForId(deviceId);
                // Based on which key was pressed, update the player avatar
                // (e.g. set the ship headings or fire lasers)
                ...
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }
    

참고: 사용자의 게임 컨트롤러 연결이 끊기면 게임을 일시중지하고 사용자에게 다시 연결할지 묻는 것이 좋습니다.