게임패드 API를 사용하여 웹 게임을 한 단계 업그레이드하는 방법을 알아보세요.
Chrome의 오프라인 페이지 이스터 에그는 역사상 가장 잘 알려진 비밀 중 하나입니다 ([citation needed]
, 극적인 효과를 위해 주장함). 스페이스 키를 누르거나 휴대기기에서 공룡을 탭하면 오프라인 페이지가 플레이 가능한 아케이드 게임으로 바뀝니다. 게임을 하고 싶을 때 오프라인 상태가 되지 않아도 된다는 사실을 알고 계실 것입니다. Chrome에서 about://dino
로 이동하거나, about://network-error/-106
로 이동하면 됩니다. 하지만 매달 2억 7천만 개의 Chrome 공룡 게임이 플레이된다는 사실을 알고 계셨나요?

아케이드 모드에서는 게임패드로 게임을 플레이할 수 있다는 사실도 알아두면 유용할 수 있습니다. 게임패드 지원은 이 글을 쓰는 시점으로부터 약 1년 전에 Reilly Grant의 커밋에서 추가되었습니다. 보시다시피 게임은 Chromium 프로젝트의 나머지 부분과 마찬가지로 완전히 오픈소스입니다. 이 게시물에서는 Gamepad API를 사용하는 방법을 보여드리겠습니다.
Gamepad API 사용
기능 감지 및 브라우저 지원
게임패드 API는 데스크톱과 모바일 모두에서 보편적으로 브라우저 지원이 우수합니다. 다음 스니펫을 사용하여 Gamepad API가 지원되는지 감지할 수 있습니다.
if ('getGamepads' in navigator) {
// The API is supported!
}
브라우저에서 게임패드를 표현하는 방법
브라우저는 게임패드를 Gamepad
객체로 나타냅니다. Gamepad
에는 다음과 같은 속성이 있습니다.
id
: 게임패드의 식별 문자열입니다. 이 문자열은 연결된 게임패드 기기의 브랜드 또는 스타일을 식별합니다.displayId
: 연결된VRDisplay
의VRDisplay.displayId
입니다 (관련된 경우).index
: 탐색기의 게임패드 색인입니다.connected
: 게임패드가 시스템에 계속 연결되어 있는지 나타냅니다.hand
: 컨트롤러를 잡고 있는 손 또는 잡을 가능성이 가장 큰 손을 정의하는 enum입니다.timestamp
: 이 게임패드의 데이터가 마지막으로 업데이트된 시간입니다.mapping
: 이 기기에 사용되는 버튼 및 축 매핑입니다("standard"
또는"xr-standard"
).pose
: WebVR 컨트롤러와 연결된 포즈 정보를 나타내는GamepadPose
객체입니다.axes
: 게임패드의 모든 축에 대한 값의 배열로,-1.0
~1.0
범위로 선형 정규화됩니다.buttons
: 게임패드의 모든 버튼에 대한 버튼 상태 배열입니다.
버튼은 디지털 (눌림 또는 눌리지 않음) 또는 아날로그 (예: 78% 눌림)일 수 있습니다. 따라서 버튼은 다음 속성을 가진 GamepadButton
객체로 보고됩니다.
pressed
: 버튼의 눌림 상태입니다 (버튼이 눌리면true
, 눌리지 않으면false
).touched
: 버튼의 터치 상태입니다. 버튼이 터치를 감지할 수 있는 경우 이 속성은 버튼이 터치되면true
이고 그렇지 않으면false
입니다.value
: 아날로그 센서가 있는 버튼의 경우 이 속성은 버튼이 눌린 양을 나타내며,0.0
~1.0
범위로 선형 정규화됩니다.hapticActuators
:GamepadHapticActuator
객체를 포함하는 배열입니다. 각 객체는 컨트롤러에서 사용할 수 있는 햅틱 피드백 하드웨어를 나타냅니다.
브라우저와 게임패드에 따라 vibrationActuator
속성이 표시될 수도 있습니다. 두 가지 종류의 진동 효과를 허용합니다.
- 듀얼 럼블: 게임패드의 각 그립에 하나씩 있는 두 개의 편심 회전 질량 액추에이터에 의해 생성되는 햅틱 피드백 효과입니다.
- 트리거 진동: 게임패드의 각 트리거에 하나씩 있는 두 개의 독립적인 모터로 생성되는 햅틱 피드백 효과입니다.
사양에서 바로 가져온 다음 개요도에서는 일반 게임패드의 버튼과 축의 매핑과 배열을 보여줍니다.
게임패드가 연결되면 알림
게임패드가 연결된 시점을 알아보려면 window
객체에서 트리거되는 gamepadconnected
이벤트를 수신 대기합니다. 사용자가 게임패드를 연결하면(USB 또는 블루투스를 통해 연결할 수 있음) 적절한 이름의 gamepad
속성에 게임패드의 세부정보가 있는 GamepadEvent
가 발생합니다.
다음은 제가 가지고 있던 Xbox 360 컨트롤러의 예시입니다 (예, 저는 복고풍 게임을 좋아합니다).
window.addEventListener('gamepadconnected', (event) => {
console.log('✅ 🎮 A gamepad was connected:', event.gamepad);
/*
gamepad: Gamepad
axes: (4) [0, 0, 0, 0]
buttons: (17) [GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton]
connected: true
id: "Xbox 360 Controller (STANDARD GAMEPAD Vendor: 045e Product: 028e)"
index: 0
mapping: "standard"
timestamp: 6563054.284999998
vibrationActuator: GamepadHapticActuator {type: "dual-rumble"}
*/
});
게임패드 연결이 끊어지면 알림
게임패드 연결 해제 알림은 연결이 감지되는 방식과 유사하게 발생합니다.
이번에는 앱이 gamepaddisconnected
이벤트를 리슨합니다. 다음 예시에서 Xbox 360 컨트롤러를 분리하면 connected
이 이제 false
이 됩니다.
window.addEventListener('gamepaddisconnected', (event) => {
console.log('❌ 🎮 A gamepad was disconnected:', event.gamepad);
/*
gamepad: Gamepad
axes: (4) [0, 0, 0, 0]
buttons: (17) [GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton, GamepadButton]
connected: false
id: "Xbox 360 Controller (STANDARD GAMEPAD Vendor: 045e Product: 028e)"
index: 0
mapping: "standard"
timestamp: 6563054.284999998
vibrationActuator: null
*/
});
게임 루프의 게임패드
게임패드를 가져오는 것은 navigator.getGamepads()
호출로 시작되며, 이 호출은 Gamepad
항목이 있는 배열을 반환합니다. Chrome의 배열은 항상 4개의 항목으로 고정됩니다. 게임패드가 0개 또는 4개 미만으로 연결된 경우 항목이 null
일 수 있습니다. 항상 배열의 모든 항목을 확인하고 게임패드는 슬롯을 '기억'하므로 항상 사용 가능한 첫 번째 슬롯에 없을 수도 있습니다.
// When no gamepads are connected:
navigator.getGamepads();
// (4) [null, null, null, null]
하나 이상의 게임패드가 연결되어 있지만 navigator.getGamepads()
에서 여전히 null
항목을 보고하는 경우 버튼을 눌러 각 게임패드를 '절전 모드 해제'해야 할 수 있습니다. 그런 다음 다음 코드와 같이 게임 루프에서 게임패드 상태를 폴링할 수 있습니다.
const pollGamepads = () => {
// Always call `navigator.getGamepads()` inside of
// the game loop, not outside.
const gamepads = navigator.getGamepads();
for (const gamepad of gamepads) {
// Disregard empty slots.
if (!gamepad) {
continue;
}
// Process the gamepad state.
console.log(gamepad);
}
// Call yourself upon the next animation frame.
// (Typically this happens every 60 times per second.)
window.requestAnimationFrame(pollGamepads);
};
// Kick off the initial game loop iteration.
pollGamepads();
진동 액추에이터
vibrationActuator
속성은 GamepadHapticActuator
객체를 반환합니다. 이 객체는 햅틱 피드백을 목적으로 힘을 가할 수 있는 모터 또는 기타 액추에이터의 구성에 해당합니다. Gamepad.vibrationActuator.playEffect()
를 호출하여 햅틱 효과를 재생할 수 있습니다. 유일하게 유효한 효과 유형은 'dual-rumble'
및 'trigger-rumble'
입니다.
지원되는 진동 효과
if (gamepad.vibrationActuator.effects.includes('trigger-rumble')) {
// Trigger rumble supported.
} else if (gamepad.vibrationActuator.effects.includes('dual-rumble')) {
// Dual rumble supported.
} else {
// Rumble effects aren't supported.
}
듀얼 진동
듀얼 럼블은 표준 게임패드의 각 핸들에 편심 회전 질량 진동 모터가 있는 햅틱 구성을 설명합니다. 이 구성에서는 모터가 전체 게임패드를 진동시킬 수 있습니다. 두 질량은 같지 않으므로 각 효과를 결합하여 더 복잡한 햅틱 효과를 만들 수 있습니다. 듀얼 진동 효과는 다음 네 가지 매개변수로 정의됩니다.
duration
: 진동 효과의 지속 시간을 밀리초 단위로 설정합니다.startDelay
: 진동이 시작될 때까지의 지연 시간을 설정합니다.strongMagnitude
및weakMagnitude
: 무거운 편심 회전 질량 모터와 가벼운 편심 회전 질량 모터의 진동 강도 수준을0.0
~1.0
범위로 정규화하여 설정합니다.
// This assumes a `Gamepad` as the value of the `gamepad` variable.
const dualRumble = (gamepad, delay = 0, duration = 100, weak = 1.0, strong = 1.0) => {
if (!('vibrationActuator' in gamepad)) {
return;
}
gamepad.vibrationActuator.playEffect('dual-rumble', {
// Start delay in ms.
startDelay: delay,
// Duration in ms.
duration: duration,
// The magnitude of the weak actuator (between 0 and 1).
weakMagnitude: weak,
// The magnitude of the strong actuator (between 0 and 1).
strongMagnitude: strong,
});
};
진동 트리거
트리거 진동은 두 개의 독립적인 모터에 의해 생성되는 햅틱 피드백 효과로, 각 모터는 게임패드의 트리거에 하나씩 있습니다.
// This assumes a `Gamepad` as the value of the `gamepad` variable.
const triggerRumble = (gamepad, delay = 0, duration = 100, weak = 1.0, strong = 1.0) => {
if (!('vibrationActuator' in gamepad)) {
return;
}
// Feature detection.
if (!('effects' in gamepad.vibrationActuator) || !gamepad.vibrationActuator.effects.includes('trigger-rumble')) {
return;
}
gamepad.vibrationActuator.playEffect('trigger-rumble', {
// Duration in ms.
duration: duration,
// The left trigger (between 0 and 1).
leftTrigger: leftTrigger,
// The right trigger (between 0 and 1).
rightTrigger: rightTrigger,
});
};
권한 정책과의 통합
게임패드 API 사양은 문자열 "gamepad"
로 식별되는 정책 제어 기능을 정의합니다. 기본 allowlist
는 "self"
입니다. 문서의 권한 정책은 해당 문서의 콘텐츠가 navigator.getGamepads()
에 액세스할 수 있는지 여부를 결정합니다. 문서에서 사용 중지된 경우 문서의 콘텐츠는 navigator.getGamepads()
를 사용할 수 없으며 gamepadconnected
및 gamepaddisconnected
이벤트도 실행되지 않습니다.
<iframe src="index.html" allow="gamepad"></iframe>
데모
다음 예시에는 게임패드 테스터 데모가 삽입되어 있습니다. 소스 코드는 Glitch에서 확인할 수 있습니다. USB 또는 블루투스를 사용하여 게임패드를 연결하고 버튼을 누르거나 축을 움직여 데모를 실행해 보세요.
보너스: web.dev에서 Chrome 공룡 게임 플레이하기
이 사이트에서 게임패드로 Chrome 공룡 게임을 플레이할 수 있습니다. 소스 코드는 GitHub에서 제공됩니다.
trex-runner.js
에서 게임패드 폴링 구현을 확인하고 키 누름을 에뮬레이션하는 방법을 참고하세요.
Chrome 공룡 게임패드 데모가 작동하도록 핵심 Chromium 프로젝트에서 Chrome 공룡 게임을 추출하고 (Arnelle Ballane의 이전 작업 업데이트) 독립형 사이트에 배치하고, 더킹 및 진동 효과를 추가하여 기존 게임패드 API 구현을 확장하고, 전체 화면 모드를 만들었습니다. Mehul Satardekar는 어두운 모드 구현에 기여했습니다. 즐거운 게임 되세요!
유용한 링크
감사의 말씀
이 문서는 프랑수아 보포르와 조 메들리가 검토했습니다. 게임패드 API 사양은 스티브 애거스턴, 제임스 홀리어, 매트 레이놀즈가 수정합니다. 이전 사양 편집자는 브랜든 존스, 스콧 그레이엄, 테드 미엘차렉입니다. 게임패드 확장 프로그램 사양은 브랜든 존스가 수정합니다. 히어로 이미지는 Laura Torrent Puig가 제작했습니다.