تشغيل لعبة الديناصور في Chrome باستخدام لوحة الألعاب

تعرَّف على كيفية استخدام واجهة برمجة التطبيقات Gamepad API لتحسين ألعاب الويب.

إنّ مفاجأة Chrome المخفية في الصفحة بلا اتصال بالإنترنت هي من أسوأ الأسرار التي تم الاحتفاظ بها في التاريخ ([citation needed]، ولكن تم تقديم هذا الادعاء لتأثيره الدرامي). إذا ضغطت على مفتاح المسافة أو نقرت على الديناصور على الأجهزة الجوّالة، ستصبح الصفحة بلا إنترنت لعبة أركيد قابلة للتشغيل. قد تعلم أنّه ليس عليك إيقاف الاتصال بالإنترنت للّعب: في Chrome، يمكنك الانتقال إلى about://dino أو الانتقال إلى about://network-error/-106. هل لديك فكرة عن عدد مستخدمي لعبة الديناصور في Chrome؟ يتم تشغيل لعبة الديناصور في Chrome 270 مليون مرة كل شهر.

صفحة Chrome بلا إنترنت مع لعبة الديناصور في Chrome
اضغط على مفتاح المسافة لبدء اللعب.

هناك حقيقة أخرى قد لا تكون على دراية بها، وهي أنّه في وضع "ألعاب الأركيد"، يمكنك تشغيل اللعبة باستخدام جهاز تحكّم. تمت إضافة ميزة استخدام وحدة التحكّم في الألعاب قبل عام تقريبًا، ويعود تاريخ كتابة هذه المقالة إلى وقت تنفيذ commit الذي أجراه Reilly Grant. كما ترى، إنّ اللعبة، مثل بقية مشروع Chromium، هي مفتوحة المصدر بالكامل. في هذه المشاركة، سأعرض لك كيفية استخدام واجهة برمجة التطبيقات Gamepad API.

استخدام Gamepad API

رصد الميزات وتوافق المتصفّح

توفّر واجهة برمجة التطبيقات Gamepad API توافقًا رائعًا مع المتصفّحات على مستوى كلٍّ من أجهزة الكمبيوتر المكتبي والأجهزة الجوّالة. يمكنك معرفة ما إذا كانت واجهة برمجة التطبيقات Gamepad API متوافقة باستخدام المقتطف التالي:

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

كيفية تمثيل المتصفّح لوحدة تحكّم

يمثّل المتصفّح لوحات الألعاب على شكل عناصر Gamepad. تتضمّن السمة Gamepad السمات التالية:

  • id: سلسلة تعريف لوحة الألعاب تحدِّد هذه السلسلة العلامة التجارية أو الطراز لجهاز وحدة التحكّم في الألعاب المتصل.
  • displayId: VRDisplay.displayId لVRDisplay المرتبط (إذا كان ذلك منطبقًا)
  • index: فهرس جهاز التحكّم في الألعاب في المتصفّح
  • connected: يشير إلى ما إذا كان جهاز التحكّم في الألعاب لا يزال متصلاً بالنظام.
  • hand: فهرس يحدّد اليد التي يتم حمل وحدة التحكّم بها أو اليد التي يُرجّح أن يتم حملها بها
  • timestamp: آخر مرة تم فيها تعديل بيانات جهاز التحكّم في الألعاب هذا
  • mapping: تعيين الأزرار والمحاور المستخدَم لهذا الجهاز، إما "standard" أو "xr-standard".
  • pose: عنصر GamepadPose يمثّل معلومات الوضع المرتبطة بجهاز تحكّم في WebVR
  • axes: صفيف من القيم لجميع محاور لوحة الألعاب، تم تسويته خطيًا ضمن النطاق -1.0‎–1.0
  • buttons: مصفوفة من حالات الأزرار لجميع أزرار لوحة الألعاب

يُرجى العلم أنّ الأزرار يمكن أن تكون رقمية (مضغوطة أو غير مضغوطة) أو تمثيلية (مثلاً، مضغوطة بنسبة% 78). لهذا السبب، يتم إدراج الأزرار كعناصر GamepadButton، مع السمات التالية:

  • pressed: حالة الضغط على الزر (true إذا تم الضغط على الزر، وfalse إذا لم يتم الضغط عليه)
  • touched: حالة الزر عند لمسه إذا كان الزر قادرًا على رصد اللمس، تكون قيمة هذه السمةtrue إذا تم لمس الزر، وfalse في غير ذلك.
  • value: بالنسبة إلى الأزرار التي تحتوي على أداة استشعار تمثيلية، تمثّل هذه السمة مقدار الضغط على الزر، ويتم تسويتها خطيًا ضمن النطاق من 0.0 إلى 1.0.
  • hapticActuators: صفيف يحتوي على عناصر GamepadHapticActuator ، يمثّل كلّ منها جهازًا للّمس التحفيزي متوفّرًا في جهاز التحكّم.

هناك عنصر إضافي قد تواجهه، استنادًا إلى المتصفّح ووحدة التحكّم في الألعاب التي تستخدمها، وهو سمة vibrationActuator. يتيح هذا الخيار نوعَين من تأثيرات الارتجاج:

  • التأثير المزدوج للاهتزاز: تأثير الملاحظات اللمسية الناتج عن محرّكَين غريبَي الشكل للكتلة الدوّارة، أحدهما في كل مقبض من مقبضَي ذراع التحكّم.
  • التأثير في الزناد: تأثير الملاحظات اللمسية الناتج عن محرّكين مستقلّين، مع محرّك واحد في كل من أدوات التحكم في لوحة الألعاب.

يعرض المخطّط التالي للنظرة العامة، المأخوذ مباشرةً من المواصفات، تعيين الأزرار والمحاور وترتيبها على جهاز تحكم ألعاب عام.

نظرة عامة تخطيطية على عمليات ربط الأزرار والمحاور لوحدة تحكم ألعاب شائعة
تمثيل مرئي لتخطيط لوحة ألعاب عادية (المصدر).

إشعار عند توصيل جهاز تحكّم في الألعاب

لمعرفة متى يتم توصيل جهاز تحكم ألعاب، انتبِه إلى حدث gamepadconnected الذي يتم تشغيله على العنصر window. عندما يربط المستخدم جهاز تحكم ألعاب، يمكن أن يحدث ذلك باستخدام USB أو باستخدام البلوتوث، يتم تشغيل GamepadEvent يحتوي على تفاصيل جهاز التحكّم في الألعاب في خاصية gamepad ذات الاسم المناسب. في ما يلي مثال من وحدة تحكّم في جهاز 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. يُرجى ملاحظة أنّه في المثال التالي، أصبح connected هو false عند فصل وحدة تحكّم Xbox 360.

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 دائمًا ثابتًا ويضم أربعة عناصر. إذا لم يتم ربط أربعة أجهزة تحكم في الألعاب أو أقل، قد يكون العنصر null فقط. احرص دائمًا على التحقّق من جميع عناصر المصفوفة، وتذكَّر أنّ أجهزة التحكّم في الألعاب "تتذكر" خانتها وقد لا تكون متاحة دائمًا في الخانة الأولى المتوفّرة.

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

إذا كان هناك جهاز تحكم ألعاب واحد أو عدة أجهزة متصلة، ولكن navigator.getGamepads() لا يزال يُبلغ عن null جهاز، قد تحتاج إلى "تنشيط" كل جهاز تحكم ألعاب من خلال الضغط على أي من أزراره. يمكنك بعد ذلك فحص حالة gamepad في حلقة اللعبة كما هو موضّح في الرمز البرمجي التالي.

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: لضبط مدة الانتظار إلى أن يبدأ الاهتزاز.
  • strongMagnitude وweakMagnitude: اضبط مستويات شدة الاهتزاز للمحركات ذات الكتلة الدوّارة المتغيّرة الأوزان، والتي تكون أثقل أو أخفّ، مع تسويتها على النطاق 0.01.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 أو البلوتوث والضغط على أي من أزراره أو تحريك أي من محاوره.

ميزة إضافية: تشغيل لعبة الديناصور في Chrome على web.dev

يمكنك تشغيل ديناصور Chrome باستخدام وحدة التحكّم في الألعاب على هذا الموقع الإلكتروني. يتوفّر رمز المصدر على GitHub. اطّلِع على عملية تنفيذ الاستطلاع في وحدة التحكّم في ألعاب الفيديو في trex-runner.js وتحقّق من كيفية محاكاة الضغط على المفاتيح.

لكي يعمل الإصدار التجريبي من وحدة تحكّم لعبة الديناصور في Chrome، أزلنا لعبة الديناصور في Chrome من مشروع Chromium الأساسي (من خلال تعديل جهد سابق من تأليف أرنيل بالاني)، ووضعناها على موقع إلكتروني مستقل، ووسّعنا تنفيذ واجهة برمجة التطبيقات الحالية لوحدة التحكّم في الألعاب من خلال إضافة تأثيرات الانحناء والاهتزاز، واخترعنا وضعًا للعرض على الشاشة الكاملة، وساهم مههول ساتارديكار في تنفيذ وضع الإضاءة المنخفضة. نتمنى لك تجربة لعب ممتعة.

الشكر والتقدير

راجع هذا المستند كلّ من فرانسوا بافوي و جو ميديل. يُعدّل مواصفات Gamepad API كلّ من Steve Agoston و James Hollyer و Matt Reynolds. محرّرو المواصفات السابقون هم براندون جونز وسكوت غراهام و تيد ميلشاريك. يُعدّل براندون جونز مواصفات إضافات لوحات الألعاب. الصورة الرئيسية من تصميم "لورا تورنت بويج".