Oyun kumandanızla Chrome dinozor oyununu oynayın

Web oyunlarınızı bir üst seviyeye çıkarmak için Gamepad API'yi nasıl kullanacağınızı öğrenin.

Chrome'un çevrimdışı sayfası olan paskalya yumurtası tarihte en kötü saklanan sırlardan biridir ([citation needed], ancak bu oyunun dramatik etkiye sahip olduğu iddia edildi). Boşluk tuşuna basarsanız veya mobil cihazlarda dinozora dokunursanız çevrimdışı sayfa, oynanabilir bir arcade oyunu haline gelir. Oyun oynamak istediğinizde internete bağlı olmanız gerekmediğini biliyor olabilirsiniz: Chrome'da about://dino'e gidebilir veya meraklıysanız about://network-error/-106'e göz atabilirsiniz. Ancak her ay 270 milyon Chrome dinozor oyunu oynandığını biliyor muydunuz?

Chrome'un Chrome dinozor oyununun bulunduğu çevrimdışı sayfası.
Oynamak için boşluk tuşuna basın.

Bilmenizin faydalı olabileceği ve farkında olmayabileceklerinizden biri de arcade modunda oyunu gamepad ile oynayabilmenizdir. Gamepad desteği, bu makalenin yazıldığı tarih itibarıyla yaklaşık bir yıl önce Reilly Grant tarafından yapılan bir taahhütte eklendi. Gördüğünüz gibi oyun, Chromium projesinin geri kalanı gibi tamamen açık kaynaktır. Bu gönderide, Gamepad API'nin nasıl kullanılacağını göstermek istiyorum.

Gamepad API'yi 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. Aşağıdaki snippet'i kullanarak Gamepad API'nin desteklenip desteklenmediğini algılayabilirsiniz:

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

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

Tarayıcı, gamepad'leri Gamepad nesnesi olarak gösterir. Gamepad aşağıdaki özelliklere sahiptir:

  • id: Gamepad için bir tanımlayıcı dizesi. Bu dize, bağlı oyun kumandası cihazının markasını veya stilini tanımlar.
  • displayId: İlişkilendirilmiş bir VRDisplay öğesine ait VRDisplay.displayId (varsa).
  • index: Gezinmedeki oyun kumandasının dizini.
  • connected: Gamepad'in sisteme bağlı olup olmadığını belirtir.
  • hand: Kumandanın hangi elde tutulduğunu veya hangi elide tutulacağını tanımlayan bir sıralama.
  • timestamp: Bu gamepad'in verilerinin en son güncellendiği zaman.
  • mapping: Bu cihazda kullanılan düğme ve eksen eşlemesidir ("standard" veya "xr-standard").
  • pose: WebVR denetleyiciyle ilişkili duruş bilgilerini temsil eden bir GamepadPose nesnesi.
  • axes: Gamepad'in tüm eksenleri için -1.0-1.0 aralığına göre doğrusal olarak normalleştirilmiş bir değer dizisi.
  • buttons: Oyun kumandasının tüm düğmelerinin düğme durumları dizisi.

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

  • pressed: Düğmenin basılı durumu (düğmeye basılmışsa true, basılı değilse false).
  • touched: Düğmenin dokunulduğu durum. Düğme dokunmayı algılayabiliyorsa bu özellik, düğmeye dokunuluyorsa true, aksi takdirde false değerini alır.
  • value: Analog sensöre sahip düğmelerde bu özellik, düğmeye basılma miktarını gösterir ve 0.01.0 aralığına doğrusal olarak normalleştirilmiştir.
  • hapticActuators: Her biri denetleyicide bulunan dokunma geri bildirimi donanımını temsil eden GamepadHapticActuator öğeleri içeren bir dizi.

Tarayıcınız ve gamepad'inize bağlı olarak karşılaşabileceğiniz bir diğer şey de vibrationActuator mülküdür. İki tür titreşim efekti sağlar:

  • Dual-Rumble: Oyun kumandasının her bir tutma noktasında birer tane olmak üzere alışılmadık dönen iki kütle aktüatörünün oluşturduğu dokunsal geribildirim efekti.
  • Tetikleme-Rumble: Oyun kumandasının tetikleyicilerinin her birinde bir motor bulunan iki bağımsız motor tarafından oluşturulan dokunsal geribildirim etkisi.

Doğrudan spesifikasyondan alınan aşağıdaki şematik genel bakış, genel bir gamepad'deki düğmelerin ve eksenlerin haritalarını ve düzenini gösterir.

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

Oyun kumandası bağlandığında bildirim

Bir gamepad'in 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ğıda, elimde bulunan bir Xbox 360 kumandasından alınmış bir örneği görebilirsiniz (Evet, retro oyunlara meraklıyım).

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

Gamepad bağlantısı kesildiğinde bildirim

Oyun kumandası bağlantı kesintilerinden haberdar olmak, bağlantıların algılanmasına benzer şekilde gerçekleşir. Bu kez uygulama gamepaddisconnected etkinliğini dinler. Aşağıdaki örnekte, Xbox 360 kumandasının fişi çekildiğinde connected değerinin false olarak nasıl değiştiğine dikkat edin.

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ı

Gamepad'i kullanmaya başlamak için navigator.getGamepads() çağrısı yapılır. Bu çağrı, Gamepad öğe içeren bir dizi döndürür. Chrome'daki dizinin uzunluğu her zaman dört öğe olarak sabitlenmiştir. Dörtten az oyun kumandası bağlanmışsa bu öğe yalnızca null olabilir. Her zaman dizinin tüm öğelerini kontrol edin. Gamepad'lerin, slotlarını "hatırladığını" ve her zaman ilk kullanılabilir slotta bulunmayabileceğini unutmayın.

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

Bir veya daha fazla gamepad bağlıysa ancak navigator.getGamepads() hâlâ null öğe bildiriyorsa her gamepad'in düğmelerinden birine basarak gamepad'i "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 aktüatörü

vibrationActuator mülkü, dokunma geri bildirimi 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. Dokunma etkileri Gamepad.vibrationActuator.playEffect() çağrılarak oynatılabilir. Geçerli tek efekt türleri 'dual-rumble' ve 'trigger-rumble''dir.

Desteklenen titreşim 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.
}

Çift titreşim

Çift titreşim, standart bir gamepad'in her koluna eksantrik dönen kütle titreşim motoru yerleştirilmiş bir dokunma yapılandırmasını ifade eder. Bu yapılandırmada, her iki motor da gamepad'in tamamını titreştirebilir. İki kütle eşit değildir. Böylece her birinin etkileri birleştirilerek daha karmaşık dokunma etkileri oluşturulabilir. Çift gürültülü efektler dört parametreyle tanımlanır:

  • duration: Titreşim etkisinin süresini milisaniye cinsinden ayarlar.
  • startDelay: Titreşimin başlatılmasına kadar geçecek süreyi 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.0-1.0 aralığına göre normalleştirilmiş olarak ayarlayın.
// 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,
  });
};

Titreşimi tetikleme

Tetik titreşimi, gamepad'in tetikleyicilerinin her birinde bulunan bir motorla iki bağımsız motor tarafından oluşturulan dokunsal geri bildirim efektidir.

// 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ı ile entegrasyon

Gamepad API spesifikasyonu, "gamepad" dizesiyle tanımlanan politika tarafından kontrol edilen bir özelliği tanımlar. Varsayılan allowlist değeri "self"'dur. Bir dokümanın izin politikası, dokümandaki herhangi bir içeriğin navigator.getGamepads() ürününe erişmesine izin verilip verilmeyeceğini belirler. Bir dokümanda devre dışı bırakılırsa dokümanda hiçbir içeriğin navigator.getGamepads() kullanmasına izin verilmez ve gamepadconnected ile gamepaddisconnected etkinlikleri tetiklenmez.

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

Demo

Aşağıdaki örnekte bir gamepad test cihazı demosu yerleştirilmiştir. Kaynak koduna Glitch'ten ulaşabilirsiniz. USB veya Bluetooth kullanarak bir gamepad bağlayın ve düğmelerinden birine basarak veya eksenlerinden birini hareket ettirerek denemeyi deneyin.

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

Bu sitede gamepad'inizle Chrome dinozor oyununu oynayabilirsiniz. Kaynak kodunu GitHub'da bulabilirsiniz. trex-runner.js içindeki oyun kumandası yoklama uygulamasına göz atın ve tuşlara basma işleminin nasıl taklit edildiğini inceleyin.

Chrome dino gamepad demosunun çalışması için Chrome dinozor oyununu ana Chromium projesinden çıkardım (Arnelle Ballane tarafından yapılan önceki bir çalışmayı güncelledim), bağımsız bir siteye yerleştirdim, eğme ve titreşim efektleri ekleyerek mevcut gamepad API uygulamasını genişlettim, tam ekran modu oluşturdum ve Mehul Satardekar karanlık mod uygulamasıyla katkıda bulundu. İyi oyunlar dileriz!

Teşekkür ederiz

Bu doküman François Beaufort ve Joe Medley tarafından incelenmiştir. Gamepad API spesifikasyonu, Steve Agoston, James Hollyer ve Matt Reynolds tarafından düzenlenmiştir. Eski spesifikasyon editörleri Brandon Jones, Scott Graham ve Ted Mielczarek'tir. Oyun kumandası uzantıları spesifikasyonu, Brandon Jones tarafından düzenlenmiştir. Laura Torrent Puig tarafından oluşturulan hero resim.