Gamepad API를 사용하여 웹 게임을 한 단계 업그레이드하는 방법을 알아보세요.
Chrome의 오프라인 페이지 이스터 에그는 역사상 가장 잘 지켜지지 않은 비밀 중 하나입니다([citation needed]
, 극적인 효과를 위해 주장함). Space 키를 누르거나 휴대기기에서 공룡을 탭하면 오프라인 페이지가 플레이 가능한 아케이드 게임이 됩니다. 게임을 플레이하고 싶을 때 오프라인으로 전환할 필요가 없다는 사실을 알고 계실 것입니다. Chrome에서는 about://dino
로 이동하거나 괴짜의 경우 about://network-error/-106
로 이동하면 됩니다. 하지만 매달 2억 7천만 건의 Chrome 공룡 게임이 플레이된다는 사실을 알고 계셨나요?
아케이드 모드에서는 게임 패드로 게임을 플레이할 수 있다는 사실도 알아두면 유용합니다. 게임패드 지원은 이 글을 작성하는 시점으로부터 약 1년 전에 Reilly Grant의 커밋에서 추가되었습니다. 보시다시피 이 게임은 나머지 Chromium 프로젝트와 마찬가지로 완전히 오픈소스입니다. 이 게시물에서는 Gamepad API를 사용하는 방법을 보여드리겠습니다.
Gamepad API 사용
기능 감지 및 브라우저 지원
Gamepad 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
속성도 발생할 수 있습니다. 두 가지 종류의 럼블 효과를 허용합니다.
- Dual-Rumble: 게임패드의 그립마다 하나씩 있는 두 개의 편심 회전 질량 액추에이터에 의해 생성되는 햅틱 효과입니다.
- 트리거 럼블: 두 개의 독립형 모터에서 생성되는 햅틱 효과로, 각 게임패드의 트리거에 모터 하나가 있습니다.
사양에서 가져온 다음 도식 개요는 일반 게임패드의 버튼과 축의 매핑과 배열을 보여줍니다.
게임패드 연결 시 알림
게임패드가 연결된 시점을 알아보려면 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
*/
});
게임 루프의 게임패드
게임패드를 가져오는 작업은 Gamepad
항목이 포함된 배열을 반환하는 navigator.getGamepads()
호출로 시작됩니다. Chrome의 배열은 항상 4개 항목으로 길이가 고정되어 있습니다. 연결된 게임패드가 0개 이하인 경우 항목이 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.
}
듀얼 럼블
Dual-rumble은 표준 게임패드의 각 핸들에 편심 회전 질량 진동 모터가 있는 햅틱 구성을 나타냅니다. 이 구성에서는 어느 모터나 전체 게임패드를 진동시킬 수 있습니다. 두 질량은 서로 다르므로 각 질량의 효과를 결합하여 더 복잡한 햅틱 효과를 만들 수 있습니다. 듀얼 럼블 효과는 다음 4가지 매개변수로 정의됩니다.
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,
});
};
권한 정책 통합
Gamepad 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를 기여했습니다. 즐거운 게임 되시기 바랍니다.
유용한 링크
감사의 말씀
이 문서는 프랑소와 보포르와 조 미들리가 검토했습니다. Gamepad API 사양은 스티브 아고스톤, 제임스 홀리어, 맷 레이놀즈가 수정합니다. 이전 사양 편집자는 브랜든 존스, 스콧 그레이엄, 테드 미엘차렉입니다. 게임패드 확장 프로그램 사양은 Brandon Jones가 수정합니다. 히어로 이미지: Laura Torrent Puig