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

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

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

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

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

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

الدمج مع سياسة الأذونات

تحدِّد مواصفات 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 بواسطة كل من ستيف أغوستون وجيمس هولير ومات رينولدز. محرّرو المواصفات السابقون هم براندون جونز وسكوت غراهام و تيد ميلشاريك. يُعدّل براندون جونز مواصفات إضافات لوحات الألعاب. الصورة الرئيسية من تصميم "لورا تورنت بويج".