여러 게임 컨트롤러 지원
컬렉션을 사용해 정리하기
내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.
대부분의 게임은 Android 기기당 단일 사용자를 지원하도록 설계되었지만
인코더-디코더 아키텍처를 기반으로 하는 게임 컨트롤러로
동일한 Android 기기에 동시에 연결되어 있어야 합니다.
이 강의에서는 단일 프로젝트에서 입력을 처리하는 몇 가지 기본 기술을 다룹니다.
여러 개의 연결된 컨트롤러에서 기기 멀티플레이어 게임을 할 수 있습니다. 여기에는
플레이어 아바타와 각 컨트롤러 기기 간의 매핑을 유지하고
컨트롤러 입력 이벤트를 적절히 처리합니다.
플레이어를 컨트롤러 기기 ID에 매핑
게임 컨트롤러가 Android 기기에 연결되면 시스템은
정수 기기 ID를 할당합니다. 연결된 기기의 기기 ID를 가져올 수 있으며
게임 컨트롤러를 사용하여 새 게임 컨트롤러를 만들 수 있습니다(게임 컨트롤러가 연결되었는지 확인 참고).InputDevice.getDeviceIds()
그런 다음 각 광고 단위를
기기 ID를 배치하고 플레이어마다 게임 작업을 개별적으로 처리합니다.
참고: Android 4.1 (API)을 실행하는 기기
레벨 16) 이상이 있는 경우,
getDescriptor()
: 고유한
영구 문자열 값입니다. 기기 ID와 달리 설명어는
입력 장치가 연결 해제되거나 다시 연결되거나
재구성됩니다
아래의 코드 스니펫은 SparseArray
를 사용하는 방법을 보여줍니다.
플레이어의 아바타를 특정 컨트롤러와 연결하는 것입니다. 이 예에서
mShips
변수는 Ship
객체 컬렉션을 저장합니다. 새로운
플레이어 아바타는 사용자가 새 컨트롤러를 연결하면 게임 내에서 생성됩니다.
연결된 컨트롤러가 삭제되면 삭제됩니다.
onInputDeviceAdded()
및 onInputDeviceRemoved()
콜백
메서드는
<ph type="x-smartling-placeholder"></ph>
다양한 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);
}
여러 컨트롤러 입력 처리
게임에서 다음 루프를 실행하여
여러 컨트롤러의 입력:
- 입력 이벤트가 발생했는지 감지합니다.
- 입력 소스와 기기 ID를 확인합니다.
- 입력 이벤트 키 코드 또는 축 값에 의해 표시되는 액션에 따라,
해당 기기 ID와 연결된 플레이어 아바타를 업데이트합니다.
- 사용자 인터페이스를 렌더링하고 업데이트합니다.
KeyEvent
및 MotionEvent
입력
이벤트에는 연결된 기기 ID가 있습니다. 게임에서
이를 통해 입력 이벤트가 발생한 컨트롤러를 판단한 다음
해당 컨트롤러와 연결된 플레이어 아바타입니다.
다음 코드 스니펫은 플레이어 아바타 참조를 가져오는 방법을 보여줍니다.
게임 컨트롤러 기기 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);
}
참고: 일반적으로 사용자가
게임 컨트롤러의 연결이 끊어지면 게임을 일시중지하고
다시 연결하려고 합니다.
이 페이지에 나와 있는 콘텐츠와 코드 샘플에는 콘텐츠 라이선스에서 설명하는 라이선스가 적용됩니다. 자바 및 OpenJDK는 Oracle 및 Oracle 계열사의 상표 또는 등록 상표입니다.
최종 업데이트: 2025-07-26(UTC)
[null,null,["최종 업데이트: 2025-07-26(UTC)"],[],[],null,["# Support multiple game controllers\n\nWhile most games are designed to support a single user per Android device,\nit's also possible to support multiple users with game controllers that are\nconnected simultaneously on the same Android device.\n\nThis lesson covers some basic techniques for handling input in your single\ndevice multiplayer game from multiple connected controllers. This includes\nmaintaining a mapping between player avatars and each controller device and\nprocessing controller input events appropriately.\n\nMap players to controller device IDs\n------------------------------------\n\nWhen a game controller is connected to an Android device, the system\nassigns it an integer device ID. You can obtain the device IDs for connected\ngame controllers by calling [InputDevice.getDeviceIds()](/reference/android/view/InputDevice#getDeviceIds()), as shown in [Verify a Game Controller is Connected](/develop/ui/views/touch-and-input/game-controllers/controller-input#input). You can then associate each\ndevice ID with a player in your game, and process game actions for each player separately.\n\n**Note:** On devices running Android 4.1 (API\nlevel 16) and higher, you can obtain an input device's descriptor using\n[getDescriptor()](/reference/android/view/InputDevice#getDescriptor()), which returns a unique\npersistent string value for the input device. Unlike a device ID, the descriptor\nvalue won't change even if the input device is disconnected, reconnected, or\nreconfigured.\n\nThe code snippet below shows how to use a [SparseArray](/reference/android/util/SparseArray)\nto associate a player's avatar with a specific controller. In this example, the\n`mShips` variable stores a collection of `Ship` objects. A new\nplayer avatar is created in-game when a new controller is attached by a user,\nand removed when its associated controller is removed.\n\nThe `onInputDeviceAdded()` and `onInputDeviceRemoved()` callback\nmethods are part of the abstraction layer introduced in\n[Supporting Controllers Across Android Versions](/training/game-controllers/compatibility#status_callbacks}). By implementing these\nlistener callbacks, your game can identify the game controller's device ID when a\ncontroller is added or removed. This detection is compatible with Android 2.3\n(API level 9) and higher. \n\n### Kotlin\n\n```kotlin\nprivate val ships = SparseArray\u003cShip\u003e()\n\noverride fun onInputDeviceAdded(deviceId: Int) {\n getShipForID(deviceId)\n}\n\noverride fun onInputDeviceRemoved(deviceId: Int) {\n removeShipForID(deviceId)\n}\n\nprivate fun getShipForID(shipID: Int): Ship {\n return ships.get(shipID) ?: Ship().also {\n ships.append(shipID, it)\n }\n}\n\nprivate fun removeShipForID(shipID: Int) {\n ships.remove(shipID)\n}\n```\n\n### Java\n\n```java\nprivate final SparseArray\u003cShip\u003e ships = new SparseArray\u003cShip\u003e();\n\n@Override\npublic void onInputDeviceAdded(int deviceId) {\n getShipForID(deviceId);\n}\n\n@Override\npublic void onInputDeviceRemoved(int deviceId) {\n removeShipForID(deviceId);\n}\n\nprivate Ship getShipForID(int shipID) {\n Ship currentShip = ships.get(shipID);\n if ( null == currentShip ) {\n currentShip = new Ship();\n ships.append(shipID, currentShip);\n }\n return currentShip;\n}\n\nprivate void removeShipForID(int shipID) {\n ships.remove(shipID);\n}\n```\n\nProcess multiple controller input\n---------------------------------\n\nYour game should execute the following loop to process\ninput from multiple controllers:\n\n1. Detect whether an input event occurred.\n2. Identify the input source and its device ID.\n3. Based on the action indicated by the input event key code or axis value, update the player avatar associated with that device ID.\n4. Render and update the user interface.\n\n[KeyEvent](/reference/android/view/KeyEvent) and [MotionEvent](/reference/android/view/MotionEvent) input\nevents have device IDs associated with them. Your game can take advantage of\nthis to determine which controller the input event came from, and update the\nplayer avatar associated with that controller.\n\nThe following code snippet shows how you might get a player avatar reference\ncorresponding to a game controller device ID, and update the game based on the\nuser's button press on that controller. \n\n### Kotlin\n\n```kotlin\noverride fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {\n if (event.source and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD) {\n event.deviceId.takeIf { it != -1 }?.also { deviceId -\u003e\n val currentShip: Ship = getShipForID(deviceId)\n // Based on which key was pressed, update the player avatar\n // (e.g. set the ship headings or fire lasers)\n return true\n }\n }\n return super.onKeyDown(keyCode, event)\n}\n```\n\n### Java\n\n```java\n@Override\npublic boolean onKeyDown(int keyCode, KeyEvent event) {\n if ((event.getSource() & InputDevice.SOURCE_GAMEPAD)\n == InputDevice.SOURCE_GAMEPAD) {\n int deviceId = event.getDeviceId();\n if (deviceId != -1) {\n Ship currentShip = getShipForId(deviceId);\n // Based on which key was pressed, update the player avatar\n // (e.g. set the ship headings or fire lasers)\n ...\n return true;\n }\n }\n return super.onKeyDown(keyCode, event);\n}\n```\n\n**Note:**As a best practice, when a user's\ngame controller disconnects, you should pause the game and ask if the user\nwants to reconnect."]]