Oyun kumandanızla Chrome dinozor oyununu oynayın

Web oyunlarınızı bir üst seviyeye taşımak için Gamepad API'sini nasıl kullanacağınızı öğrenin.

Chrome'un çevrimdışı sayfası paskalya yumurtası, tarihteki en kötü saklanan sırlardan biridir ([citation needed], ancak bu kadar etkili olduğu iddia edilmiştir). Boşluk tuşuna basarsanız veya mobil cihazlarda dinozora dokunursanız çevrimdışı sayfa, oynanabilir bir arcade oyunu haline gelir. Oyun oynamak istediğinizde çevrimdışı olmanıza gerek olmadığının farkında olabilirsiniz: Chrome'da about://dino bölümüne gidebilir ya da meraklıysanız about://network-error/-106 sitesine göz atabilirsiniz. Peki, her ay 270 milyon Chrome dinozor oyununun oynandığını biliyor muydunuz?

Chrome'un çevrimdışı dinozor oyununu içeren sayfa.
Oynamak için boşluk tuşuna basın.

Muhtemelen bilmenizin daha yararlı olduğu, sizin de farkında olmayabileceğiniz bir diğer gerçek de, Arcade modunda oyunu oyun kumandasıyla oynayabileceğinizdir. Oyun kumandası desteği yaklaşık bir yıl önce bu yazı yazıldığında Reilly Grant tarafından yapılan bir taahhütte eklendi. Gördüğünüz gibi bu oyun, Chromium projesinin geri kalanı gibi tamamen açık kaynaklıdır. Bu yayında, Gamepad API'yi nasıl kullanacağınızı göstermek istiyorum.

Gamepad API'sini kullanma

Özellik algılama ve tarayıcı desteği

Gamepad API, hem masaüstü hem de mobil cihazlarda evrensel olarak mükemmel tarayıcı desteğine sahiptir. Oyun Kumandası API'sinin desteklenip desteklenmediğini aşağıdaki snippet'i kullanarak tespit edebilirsiniz:

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

Tarayıcı, oyun kumandasını nasıl temsil eder?

Tarayıcı, oyun kumandalarını Gamepad nesneleri olarak temsil eder. Gamepad aşağıdaki özelliklere sahiptir:

  • id: Oyun kumandası için bir tanımlama dizesi. Bu dize, bağlı oyun kumandası cihazının markasını veya stilini tanımlar.
  • displayId: İlişkili bir VRDisplay için VRDisplay.displayId (varsa).
  • index: Gezinmedeki oyun kumandasının dizini.
  • connected: Oyun kumandasının sisteme hâlâ bağlı olup olmadığını gösterir.
  • hand: Kumandanın tutulduğu veya tutulma olasılığının en yüksek olduğu eli tanımlayan enum.
  • timestamp: Bu oyun kumandasına ilişkin verilerin en son güncellenme zamanı.
  • mapping: Bu cihaz için kullanılan düğme ve eksen eşlemesi ("standard" veya "xr-standard").
  • pose: WebVR denetleyicisiyle ilişkili poz bilgilerini temsil eden bir GamepadPose nesnesi.
  • axes: Oyun kumandasının tüm eksenleri için -1.01.0 aralığına doğrusal olarak normalleştirilmiş olan bir değer dizisi.
  • buttons: Oyun kumandasının tüm düğmeleri için düğme durumları dizisi.

Düğmelerin dijital (basılmış veya basılmamış) ya da analog (örneğin, %78'e basılmış) olabileceğini unutmayın. Bu nedenle düğmeler aşağıdaki özelliklere sahip GamepadButton nesneleri olarak raporlanır:

  • pressed: Düğmenin basılma durumu (düğmeye basılmışsa true ve basılı değilse false).
  • touched: Düğmenin dokunulan durumu. Düğme, dokunmayı algılayabiliyorsa düğmeye dokunulduğunda bu özellik true, aksi takdirde false olur.
  • value: Analog sensör içeren düğmelerde bu özellik, düğmeye basılan süreyi 0.01.0 aralığında doğrusal olarak normalleştirilmiş olarak gösterir.
  • hapticActuators: GamepadHapticActuator nesneleri içeren bir dizidir. Bu nesnelerin her biri, denetleyicide bulunan dokunsal geri bildirim donanımını temsil eder.

Tarayıcınıza ve sahip olduğunuz oyun kumandasına bağlı olarak karşılaşabileceğiniz diğer bir özellik de vibrationActuator özelliğidir. İki tür titreşim efektine olanak tanır:

  • Dual-Rumble: Oyun kumandasının her tutuşunda birer tane olmak üzere iki eksantrik dönen kütle etkinleştiricisinin oluşturduğu dokunsal geri bildirim efekti.
  • Tetikleyici-Rumble: Her bir oyun kumandasının tetikleyicisinde birer motor bulunan iki bağımsız motor tarafından oluşturulan dokunsal geri bildirim efekti.

Doğrudan spesifikasyondan alınan aşağıdaki şematik genel bakış, genel bir oyun kumandasında düğmeler ve eksenlerin eşlemesini ve düzenlemesini göstermektedir.

Yaygın bir oyun kumandasının düğme ve eksen eşlemelerine şematik genel bakış.
Standart oyun kumandası düzeninin görsel temsili (Kaynak).

Oyun kumandası bağlandığında bildirim

Bir oyun kumandasının ne zaman bağlandığını öğrenmek için window nesnesinde tetiklenen gamepadconnected etkinliğini dinleyin. Kullanıcı, USB veya Bluetooth kullanarak bir oyun kumandasını bağladığında, oyun kumandasının ayrıntılarını uygun şekilde adlandırılmış gamepad özelliğinde içeren bir GamepadEvent tetiklenir. Aşağıdaki örnekte, benim bıraktığım bir Xbox 360 kumanda örneğini görebilirsiniz (evet, retro oyunlarını seviyorum).

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"}
  */
});

Oyun kumandasının bağlantısı kesildiğinde bildirim

Oyun kumandası bağlantı kopmaları hakkında bildirim alma, bağlantıların algılanma yöntemine benzer şekilde gerçekleşir. Uygulama bu sefer gamepaddisconnected etkinliğini dinler. Aşağıdaki örnekte, Xbox 360 kumandayı çıkardığımda connected artık false olarak gösterilmiştir.

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
  */
});

Oyun döngünüzdeki oyun kumandası

Bir oyun kumandasını tutmak, Gamepad öğeli bir dizi döndüren navigator.getGamepads() çağrısıyla başlar. Chrome'daki dizinin her zaman sabit uzunluğu dört öğedir. Dörtten az oyun kumandası bağlıysa öğe sadece null olabilir. Dizideki tüm öğeleri her zaman kontrol ettiğinizden emin olun. Oyun kumandalarının slotlarını "hatırladığını" ve her zaman ilk müsait slotta bulunmayabileceğini unutmayın.

// When no gamepads are connected:
navigator.getGamepads();
// (4) [null, null, null, null]

Bir veya birkaç oyun kumandası bağlı olmasına rağmen navigator.getGamepads() hâlâ null öğe bildiriyorsa düğmelerinden birine basarak her bir oyun kumandasını "uyandırmanız" gerekebilir. Daha sonra, aşağıdaki kodda gösterildiği gibi oyun döngünüzdeki oyun kumandası durumlarını yoklayabilirsiniz.

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();

Titreşim başlatıcı

vibrationActuator özelliği, dokunsal geribildirim amacıyla kuvvet uygulayabilen motorların veya diğer aktüatörlerin yapılandırmasına karşılık gelen bir GamepadHapticActuator nesnesi döndürür. Dokunsal efektleri, Gamepad.vibrationActuator.playEffect() çağırarak oynatabilirsiniz. Tek geçerli efekt türü 'dual-rumble'. Dual-rumble, standart oyun kumandasının her kolunda eksantrik dönen kütle titreşim motoruyla dokunsal bir yapılandırmayı tanımlar. Bu yapılandırmada her iki motor da tüm oyun kumandasını titreşebilir. Bu iki kütle eşit değildir. Böylece her birinin etkileri daha karmaşık dokunsal efektler oluşturmak üzere birleştirilebilir. Dual-rumble efektleri dört parametreyle tanımlanır:

  • duration: Titreşim efektinin süresini milisaniye cinsinden ayarlar.
  • startDelay: Titreşim başlatılana kadar geçecek gecikme süresini ayarlar.
  • strongMagnitude ve weakMagnitude: Daha ağır ve daha hafif eksantrik dönen kütle motorları için titreşim yoğunluğu seviyelerini 0.01.0 aralığına normalleştirilmiş olarak ayarlayın.

Desteklenen ses efektleri

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

// 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,
  });
};

Takılmayı tetikle

// 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,
  });
};

İzin Politikasıyla Entegrasyon

Gamepad API spesifikasyonu, "gamepad" dizesiyle tanımlanan politika kontrollü bir özelliği tanımlar. Varsayılan allowlist değeri "self"'dir. Bir dokümanın izin politikası, söz konusu dokümandaki herhangi bir içeriğin navigator.getGamepads() ürününe erişmesine izin verilip verilmediğini belirler. Herhangi bir dokümanda devre dışı bırakılırsa dokümandaki hiçbir içeriğin navigator.getGamepads() kullanmasına izin verilmez ve gamepadconnected ve gamepaddisconnected etkinlikleri etkinleşmez.

<iframe src="index.html" allow="gamepad"></iframe>

Demo

Aşağıdaki örneğe bir oyun kumandası test kullanıcısı demosu yerleştirilmiştir. Kaynak kodu Glitch'te mevcuttur. USB veya Bluetooth kullanarak bir oyun kumandası bağlayıp düğmelerinden birine basarak veya eksenini hareket ettirerek demoyu deneyin.

Bonus: web.dev'de Chrome Dinozor oyununu oynayın

Bu sitede oyun kumandanızla Chrome Dinozor'u oynayabilirsiniz. Kaynak kodu GitHub'da kullanılabilir. trex-runner.js'te oyun kumandası yoklama uygulamasına göz atın ve tuşlara basmaları nasıl taklit ettiğine dikkat edin.

Chrome dinozor oyun kumandası demosunun çalışması için Chrome dinozor oyununu temel Chromium projesinden söktüm (Arnelle Ballane tarafından yapılan önceki bir işlemi güncelleyerek), bunu bağımsız bir siteye yerleştirdim, oyun kısılması ve titreşim efektleri ekleyerek mevcut oyun kumandası API uygulamasını genişlettim, tam ekran modu oluşturdum ve Mehul Satardekar uygulamasına katkıda bulundum. İyi oyunlar!

Teşekkür

Bu belge, François Beaufort ve Joe Medley tarafından incelendi. Gamepad API spesifikasyonu Steve Agoston, James Hollyer ve Matt Reynolds tarafından düzenlenmiştir. Eski teknik editörler ise Brandon Jones, Scott Graham ve Ted Mielczarek'tir. Oyun Kumandası Uzantıları spesifikasyonu Brandon Jones tarafından düzenlenmiştir. Laura Torrent Puig'in hero resmi.