게임패드로 Chrome 공룡 게임을 플레이하세요.

Gamepad API를 사용하여 웹 게임을 한 단계 업그레이드하는 방법을 알아보세요.

Chrome의 오프라인 페이지 이스터 에그는 역사상 최악의 비밀 중 하나이지만 ([citation needed]이지만 극적인 효과를 발휘한다고 주장함) Space 키를 누르거나 휴대기기에서 공룡을 탭하면 오프라인 페이지가 플레이 가능한 아케이드 게임이 됩니다. 플레이하고 싶을 때 오프라인으로 전환할 필요가 없다는 사실을 알고 계실 것입니다. Chrome에서는 about://dino로 이동하거나 about://network-error/-106로 이동하면 됩니다. 하지만 매월 진행되는 Chrome 공룡 게임 2억 7천만 건이 있다는 사실을 알고 계셨나요?

Chrome 공룡 게임이 포함된 Chrome 오프라인 페이지입니다.
플레이하려면 스페이스바를 누르세요.

아케이드 모드에서 게임패드로 게임을 플레이할 수 있다는 사실도 알고 있으면 더 유용하고 모르고 지나칠 수 있는 또 다른 사실입니다. 게임패드 지원은 이 문서의 작성 시점을 기준으로 약 1년 전 라일리 그랜트커밋에 추가되었습니다. 보다시피 이 게임은 나머지 Chromium 프로젝트와 마찬가지로 완전히 오픈소스입니다. 이 게시물에서는 Gamepad API를 사용하는 방법을 알려 드리겠습니다.

Gamepad API 사용

기능 감지 및 브라우저 지원

Gamepad API는 데스크톱과 모바일 모두에서 전반적으로 뛰어난 브라우저 지원을 제공합니다. 다음 스니펫을 사용하여 Gamepad API가 지원되는지 감지할 수 있습니다.

if ('getGamepads' in navigator) {
  // The API is supported!
}

브라우저가 게임패드를 나타내는 방식

브라우저는 게임패드를 Gamepad 객체로 표시합니다. Gamepad에는 다음과 같은 속성이 있습니다.

  • id: 게임패드의 식별 문자열입니다. 이 문자열은 연결된 게임패드 기기의 브랜드나 스타일을 식별합니다.
  • displayId: 연결된 VRDisplayVRDisplay.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개 이하인 경우 항목은 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'입니다. 이중 럼블은 표준 게임패드의 각 핸들에 특이한 회전 대량 진동 모터가 있는 햅틱 구성을 말합니다. 이 구성에서 두 모터 모두 게임패드 전체를 진동할 수 있습니다. 두 질량은 같지 않으므로 각 질량의 효과를 결합하여 더 복잡한 햅틱 효과를 만들 수 있습니다. 이중 럼블 효과는 다음 네 가지 매개변수로 정의됩니다.

  • duration: 진동 효과의 지속 시간을 밀리초 단위로 설정합니다.
  • startDelay: 진동이 시작될 때까지의 지연 시간을 설정합니다.
  • strongMagnitudeweakMagnitude: 0.0~1.0 범위로 정규화된 더 무겁거나 더 가벼운 이심 회전 질량 모터의 진동 강도 수준을 설정합니다.

지원되는 럼블 효과

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

듀얼 럼블

// 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()를 사용할 수 없으며 gamepadconnectedgamepaddisconnected 이벤트가 실행되지 않습니다.

<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 사양은 Steve Agoston, James Hollyer, Matt Reynolds가 수정했습니다. 이전의 사양 편집자는 브랜든 존스, 스콧 그레이엄, 테드 미엘차렉입니다. 게임패드 확장 프로그램 사양은 Brandon Jones가 편집합니다. Laura Torrent Puig의 히어로 이미지