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

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

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

Chrome 공룡 게임이 포함된 Chrome 오프라인 페이지
스페이스바를 눌러 재생하세요.

아케이드 모드에서는 게임패드로 게임을 플레이할 수 있다는 사실도 알아두면 유용할 수 있습니다. 게임패드 지원은 이 글을 쓰는 시점으로부터 약 1년 전에 Reilly Grant커밋에서 추가되었습니다. 보시다시피 게임은 Chromium 프로젝트의 나머지 부분과 마찬가지로 완전히 오픈소스입니다. 이 게시물에서는 Gamepad API를 사용하는 방법을 보여드리겠습니다.

Gamepad API 사용

기능 감지 및 브라우저 지원

게임패드 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개 또는 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: 진동이 시작될 때까지의 지연 시간을 설정합니다.
  • strongMagnitudeweakMagnitude: 무거운 편심 회전 질량 모터와 가벼운 편심 회전 질량 모터의 진동 강도 수준을 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()를 사용할 수 없으며 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는 어두운 모드 구현에 기여했습니다. 즐거운 게임 되세요!

감사의 말씀

이 문서는 프랑수아 보포르조 메들리가 검토했습니다. 게임패드 API 사양은 스티브 애거스턴, 제임스 홀리어, 매트 레이놀즈가 수정합니다. 이전 사양 편집자는 브랜든 존스, 스콧 그레이엄, 테드 미엘차렉입니다. 게임패드 확장 프로그램 사양은 브랜든 존스가 수정합니다. 히어로 이미지는 Laura Torrent Puig가 제작했습니다.