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

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

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

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

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

استخدام واجهة برمجة تطبيقات Gamepad

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

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

  • duration: لضبط مدة تأثير الاهتزاز بالمللي ثانية.
  • startDelay: يضبط هذا الخيار مدة التأخير إلى أن يبدأ الاهتزاز.
  • strongMagnitude وweakMagnitude: يجب ضبط مستويات كثافة الاهتزاز للمحرّكات ذات الكتلة الدوّارة الغريبة الأثقل والأخف، الأخف وزنًا، مع تسوية النطاق بين 0.0 و1.0.

تأثيرات الرنين المتوافقة

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.
}

رميات ثنائية

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