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

مارسين فيشاري
مارسين ويشاري

مقدمة

دع المبتدئين يحتفظون بلوحات مفاتيحهم من أجل ألعاب المغامرات، وأطراف أصابعهم متعددة اللمسات الحساسة لقطع الفاكهة، وأجهزة استشعار الحركة الجديدة والعصرية للتظاهر بالقدرة على الرقص مثل "مايكل جاكسون". (News Flash: لا يمكنه ذلك.) لكنك مختلف. أنت أفضل. أنت محترف. تبدأ الألعاب وتنتهي بلوحة ألعاب في متناول يدك.

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

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

رسومات الشعار المبتكرة في Google لعام 2012 ضمن الحواجز
رسومات الشعار المبتكرة من Google لعام 2012

مختبِر لوحة الألعاب

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

ما هي المتصفحات المتوافقة معه حاليًا؟

التوافق مع المتصفح

  • 21
  • 12
  • 29
  • 10.1

المصدر

ما هي لوحات الألعاب التي يمكن استخدامها؟

بشكل عام، من المفترض أن تعمل أي وحدة تحكُّم حديثة متوافقة مع نظامك. لقد اختبرنا العديد من وحدات التحكّم في الألعاب من وحدات تحكّم USB غير معروفة على جهاز كمبيوتر شخصي، من خلال وحدات تحكّم ألعاب PlayStation 2 متّصلة عبر مفتاح إلكتروني لجهاز Mac، وصولاً إلى وحدات التحكّم التي تتضمّن بلوتوث والمقترنة بكمبيوتر دفتري بنظام التشغيل Chrome.

أجهزة الألعاب
وحدات الألعاب

هذه صورة لبعض وحدات التحكم التي استخدمناها لاختبار رسومات الشعار المبتكرة: "نعم، يا أمي، هذا ما أفعله في العمل". إذا كانت وحدة التحكم لا تعمل، أو إذا تم تعيين عناصر التحكم بشكل غير صحيح، يرجى الإبلاغ عن خطأ في Chrome أو Firefox . (يُرجى اختبار الإصدار الأحدث من كل متصفح للتأكّد من أنه لم يتم إصلاحه بالفعل.)

ميزة رصد واجهة برمجة التطبيقات الخاصة بلوحة الألعاب<

السهولة بما فيه الكفاية في Chrome:

var gamepadSupportAvailable = !!navigator.webkitGetGamepads || !!navigator.webkitGamepads;

لا يبدو أنه من الممكن اكتشاف هذا في فايرفوكس حتى الآن – فكل شيء يعتمد على الحدث، ويجب إرفاق جميع معالِجات الأحداث بالنافذة، مما يمنع أي أسلوب نموذجي لاكتشاف معالِجات الأحداث من العمل.

لكننا متأكّدون من أنّ هذا الوضع مؤقّت. تقدّم لك أداة Modernizr الرائعة واحدة

var gamepadSupportAvailable = Modernizr.gamepads;

التعرّف على لوحات الألعاب المرتبطة

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

بعد إزالة هذه العقبة (عذرًا...)، لا يزال هناك المزيد ينتظرك.

إنشاء استطلاعات

عند تنفيذ واجهة برمجة التطبيقات في Chrome، تظهر وظيفة "navigator.webkitGetGamepads()" التي يمكنك استخدامها للحصول على قائمة بجميع لوحات الألعاب المتصلة حاليًا بالنظام، بالإضافة إلى حالتها الحالية (الأزرار + العصي). سيتم عرض أول لوحة ألعاب متصلة كأول إدخال في المصفوفة، وهكذا.

(تم مؤخرًا استبدال استدعاء الدالة هذا مصفوفة يمكنك الوصول إليها مباشرةً – navigator.webkitGamepads[]. اعتبارًا من أوائل آب (أغسطس) 2012، لا يزال الدخول إلى هذه الصفيفة ضروريًا في الإصدار 21 من Chrome، بينما يعمل استدعاء الدالة في الإصدار 22 من Chrome والإصدارات الأحدث. ومن الآن فصاعدًا، سيكون استدعاء الدالة هو الطريقة الموصى بها لاستخدام واجهة برمجة التطبيقات، وسيتدفق ببطء إلى جميع متصفحات Chrome المثبتة).

تتطلّب منك المواصفات الجديدة التي تم تنفيذها حتى الآن التحقّق باستمرار من حالة لوحات الألعاب المتصلة (ومقارنتها بالوحدة السابقة إذا لزم الأمر)، بدلاً من تنشيط الأحداث عند حدوث تغيير. لقد اعتمدنا على requestAnimationFrame() لإعداد الاستطلاعات بأكثر الطرق فعالية وكفاءة. بالنسبة إلى رسومات الشعار المبتكرة، فعلى الرغم من توفُّر حلقة requestAnimationFrame() لإتاحة الصور المتحركة في الوقت الحالي، أنشأنا رسمًا ثانيًا منفصلاً تمامًا، حيث كان من الأسهل ترميزه ولن يؤثر في الأداء بأي شكل من الأشكال.

إليك الرمز من المختبِر:

/**
 * Starts a polling loop to check for gamepad state.
 */
startPolling: function() {
    // Don't accidentally start a second loop, man.
    if (!gamepadSupport.ticking) {
    gamepadSupport.ticking = true;
    gamepadSupport.tick();
    }
},

/**
 * Stops a polling loop by setting a flag which will prevent the next
 * requestAnimationFrame() from being scheduled.
 */
stopPolling: function() {
    gamepadSupport.ticking = false;
},

/**
 * A function called with each requestAnimationFrame(). Polls the gamepad
 * status and schedules another poll.
 */
tick: function() {
    gamepadSupport.pollStatus();
    gamepadSupport.scheduleNextTick();
},

scheduleNextTick: function() {
    // Only schedule the next frame if we haven't decided to stop via
    // stopPolling() before.
    if (gamepadSupport.ticking) {
    if (window.requestAnimationFrame) {
        window.requestAnimationFrame(gamepadSupport.tick);
    } else if (window.mozRequestAnimationFrame) {
        window.mozRequestAnimationFrame(gamepadSupport.tick);
    } else if (window.webkitRequestAnimationFrame) {
        window.webkitRequestAnimationFrame(gamepadSupport.tick);
    }
    // Note lack of setTimeout since all the browsers that support
    // Gamepad API are already supporting requestAnimationFrame().
    }
},

/**
 * Checks for the gamepad status. Monitors the necessary data and notices
 * the differences from previous state (buttons for Chrome/Firefox,
 * new connects/disconnects for Chrome). If differences are noticed, asks
 * to update the display accordingly. Should run as close to 60 frames per
 * second as possible.
 */
pollStatus: function() {
    // (Code goes here.)
},

إذا كنت مهتمًا فقط بلوحة ألعاب واحدة، قد يكون الحصول على بياناتها بسيطًا مثل:

var gamepad = navigator.webkitGetGamepads && navigator.webkitGetGamepads()[0];

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

فعاليات

يستخدم متصفّح Firefox طريقة بديلة وأفضل موضّحة في مواصفات واجهة برمجة تطبيقات Gamepad API. وبدلاً من أن يطلب منك إجراء استطلاع رأي، يعرض حدثَين، وهما MozGamepadConnected وMozGamepadDisconnected، يتم تنشيطهما عند توصيل وحدة تحكّم بالألعاب (أو بشكل أدق، توصيلها وإعلانها من خلال الضغط على أيّ من أزرارها) أو فصلها عن مصدر الطاقة. يتم تمرير كائن لوحة الألعاب الذي سيظلّ يعكس حالته المستقبلية كمَعلمة .gamepad لكائن الحدث.

من رمز المصدر للمختبِر:

/**
 * React to the gamepad being connected. Today, this will only be executed
 * on Firefox.
 */
onGamepadConnect: function(event) {
    // Add the new gamepad on the list of gamepads to look after.
    gamepadSupport.gamepads.push(event.gamepad);

    // Start the polling loop to monitor button changes.
    gamepadSupport.startPolling();

    // Ask the tester to update the screen to show more gamepads.
    tester.updateGamepads(gamepadSupport.gamepads);
},

ملخّص

في النهاية، تبدو دالة التهيئة في أداة الاختبار التي تدعم كلا الأسلوبين على النحو التالي:

/**
 * Initialize support for Gamepad API.
 */
init: function() {
    // As of writing, it seems impossible to detect Gamepad API support
    // in Firefox, hence we need to hardcode it in the third clause.
    // (The preceding two clauses are for Chrome.)
    var gamepadSupportAvailable = !!navigator.webkitGetGamepads ||
        !!navigator.webkitGamepads ||
        (navigator.userAgent.indexOf('Firefox/') != -1);

    if (!gamepadSupportAvailable) {
    // It doesn't seem Gamepad API is available – show a message telling
    // the visitor about it.
    tester.showNotSupported();
    } else {
    // Firefox supports the connect/disconnect event, so we attach event
    // handlers to those.
    window.addEventListener('MozGamepadConnected',
                            gamepadSupport.onGamepadConnect, false);
    window.addEventListener('MozGamepadDisconnected',
                            gamepadSupport.onGamepadDisconnect, false);

    // Since Chrome only supports polling, we initiate polling loop straight
    // away. For Firefox, we will only do it if we get a connect event.
    if (!!navigator.webkitGamepads || !!navigator.webkitGetGamepads) {
        gamepadSupport.startPolling();
    }
    }
},

معلومات لوحة الألعاب

سيتم تمثيل كل لوحة ألعاب متصلة بالنظام بكائن يشبه ما يلي:

id: "PLAYSTATION(R)3 Controller (STANDARD GAMEPAD Vendor: 054c Product: 0268)"
index: 1
timestamp: 18395424738498
buttons: Array[8]
    0: 0
    1: 0
    2: 1
    3: 0
    4: 0
    5: 0
    6: 0.03291
    7: 0
axes: Array[4]
    0: -0.01176
    1: 0.01961
    2: -0.00392
    3: -0.01176

المعلومات الأساسية

الحقول القليلة الأولى هي بيانات تعريف بسيطة:

  • id: وصف نصي للوحة الألعاب
  • index: عدد صحيح مفيد للتمييز بين لوحات الألعاب المختلفة المتصلة بكمبيوتر واحد
  • timestamp: الطابع الزمني لآخر تحديث لحالة الزر/المحاور (غير متاح حاليًا إلا في Chrome)

الأزرار والعصي

لم تكن أجهزة الألعاب اليوم مطابقة تمامًا لأسلوب جدّك في إنقاذ الأميرة في القلعة الخاطئة، فهي تحتوي عادةً على ستة عشر زرًا منفصلاً على الأقل (بعضها منفصل وآخر تناظري)، بالإضافة إلى عصّيَين تناظريين. ستخبرك واجهة برمجة تطبيقات Gamepad عن جميع الأزرار والعصي التناظرية التي يتضمّنها نظام التشغيل.

بعد الاطّلاع على الحالة الحالية في عنصر لوحة الألعاب، يمكنك الوصول إلى الأزرار من خلال .buttons[] والعصي من خلال صفائف .axes[]. فيما يلي ملخص مرئي لما تتوافق معه:

الرسم التخطيطي للوحة الألعاب
الرسم البياني للوحة الألعاب

تطلب المواصفات من المتصفح تعيين أول ستة عشر زرًا وأربعة محاور لإجراء ما يلي:

gamepad.BUTTONS = {
    FACE_1: 0, // Face (main) buttons
    FACE_2: 1,
    FACE_3: 2,
    FACE_4: 3,
    LEFT_SHOULDER: 4, // Top shoulder buttons
    RIGHT_SHOULDER: 5,
    LEFT_SHOULDER_BOTTOM: 6, // Bottom shoulder buttons
    RIGHT_SHOULDER_BOTTOM: 7,
    SELECT: 8,
    START: 9,
    LEFT_ANALOGUE_STICK: 10, // Analogue sticks (if depressible)
    RIGHT_ANALOGUE_STICK: 11,
    PAD_TOP: 12, // Directional (discrete) pad
    PAD_BOTTOM: 13,
    PAD_LEFT: 14,
    PAD_RIGHT: 15
};

gamepad.AXES = {
    LEFT_ANALOGUE_HOR: 0,
    LEFT_ANALOGUE_VERT: 1,
    RIGHT_ANALOGUE_HOR: 2,
    RIGHT_ANALOGUE_VERT: 3
};

سيتم إلحاق الأزرار والمحاور الإضافية تلك بالأزرار أعلاه. يرجى العلم أنه ليس هناك ستة عشر زرًا ولا أربعة محاور مضمونة، على الرغم من ذلك - كن جاهزًا لأن بعض منها غير محدد ببساطة.

يمكن للأزرار أن تحدد قيمًا من 0.0 (غير مضغوط) إلى 1.0 (يتم الضغط عليها بالكامل). ينتقل المحاور من -1.0 (اليسار أو أعلى تمامًا) إلى 0.0 (منتصف) إلى 1.0 (إلى اليمين أو الأسفل تمامًا).

تناظرية أم منفصلة؟

عادةً، يمكن أن يكون كل زر تناظري - وهذا أمر شائع إلى حد ما في أزرار الكتف، على سبيل المثال. لذلك، من الأفضل تعيين حد بدلاً من مجرد مقارنته بصراحة مع 1.00 (ماذا لو حدث أن الزر التناظري غير نظيف إلى حد ما؟ وقد لا يصل إلى 1.00 أبدًا). في رسومات الشعار المبتكرة، يتم ذلك بالطريقة التالية:

gamepad.ANALOGUE_BUTTON_THRESHOLD = .5;

gamepad.buttonPressed_ = function(pad, buttonId) {
    return pad.buttons[buttonId] &&
            (pad.buttons[buttonId] > gamepad.ANALOGUE_BUTTON_THRESHOLD);
};

يمكنك أن تفعل الشيء نفسه لتحويل أعواد تناظرية إلى أذرع تحكم رقمية. بالتأكيد، توجد دائمًا لوحة رقمية (لوحة التحكم)، ولكن قد لا تحتوي وحدة التحكم في الألعاب على واحدة. إليك التعليمة البرمجية للتعامل مع ذلك:

gamepad.AXIS_THRESHOLD = .75;

gamepad.stickMoved_ = function(pad, axisId, negativeDirection) {
    if (typeof pad.axes[axisId] == 'undefined') {
    return false;
    } else if (negativeDirection) {
    return pad.axes[axisId] < -gamepad.AXIS_THRESHOLD;
    } else {
    return pad.axes[axisId] > gamepad.AXIS_THRESHOLD;
    }
};

الضغط على الأزرار وحركات العصي

فعاليات

في بعض الحالات، مثل ألعاب محاكاة الطيران، يكون من المنطقي التحقق باستمرار من مواضع العصا أو الضغط على الأزرار والتفاعل معها، ولكن بالنسبة لرسومات الشعار المبتكرة في سباقات الحواجز 2012؟ قد تتساءل: لماذا أحتاج إلى التحقق من الأزرار في كل إطار على حدة؟ لماذا لا يمكنني عرض أحداث مثل ما أفعله مع لوحة المفاتيح أو الماوس للأعلى أو للأسفل؟

يسرني إخبارك بأنّه يمكنك تحقيق ذلك. الأخبار السيئة هي - في المستقبل. هذه هي المواصفات، ولكن لم يتم تطبيقها في أي متصفح بعد.

إنشاء استطلاعات

في غضون ذلك، تقارن طريقك بين الحالة الحالية والسابقة، ووظائف الاتصال إذا لاحظت أي اختلاف. مثال:

if (buttonPressed(pad, 0) != buttonPressed(oldPad, 0)) {
    buttonEvent(0, buttonPressed(pad, 0) ? 'down' : 'up');
}
for (var i in gamepadSupport.gamepads) {
    var gamepad = gamepadSupport.gamepads[i];

    // Don't do anything if the current timestamp is the same as previous
    // one, which means that the state of the gamepad hasn't changed.
    // This is only supported by Chrome right now, so the first check
    // makes sure we're not doing anything if the timestamps are empty
    // or undefined.
    if (gamepadSupport.prevTimestamps[i] &&
        (gamepad.timestamp == gamepadSupport.prevTimestamps[i])) {
    continue;
    }
    gamepadSupport.prevTimestamps[i] = gamepad.timestamp;

    gamepadSupport.updateDisplay(i);
}

أسلوب استخدام لوحة المفاتيح أولاً في رسومات الشعار المبتكرة لسلسلة حواجز 2012

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

  1. لا تحتاج رسومات الشعار المبتكرة سوى إلى ثلاثة أزرار، اثنان للجري وآخر للقفز، ولكن من المرجّح أن تتضمّن لوحة الألعاب العديد من الأزرار. لذلك قمنا بتعيين جميع الأزرار الست عشرة المعروفة وعصاتين معروفتين على تلك الدوال المنطقية الثلاث بطريقة نعتقد أنها أكثر منطقية، بحيث يمكن للأشخاص إجراءها: التبديل بين أزرار A/B، وأزرار الكتف البديلة، والضغط على اليسار/اليمين على لوحة التحكم، أو التأرجح إما بالعصا بعنف لليسار واليمين (بعضها بالطبع سيكون أكثر فعالية من غيرها). مثال:

    newState[gamepad.STATES.LEFT] =
        gamepad.buttonPressed_(pad, gamepad.BUTTONS.PAD_LEFT) ||
        gamepad.stickMoved_(pad, gamepad.AXES.LEFT_ANALOGUE_HOR, true) ||
        gamepad.stickMoved_(pad, gamepad.AXES.RIGHT_ANALOGUE_HOR, true),
    
    newState[gamepad.STATES.PRIMARY_BUTTON] =
        gamepad.buttonPressed_(pad, gamepad.BUTTONS.FACE_1) ||
        gamepad.buttonPressed_(pad, gamepad.BUTTONS.LEFT_SHOULDER) ||
        gamepad.buttonPressed_(pad, gamepad.BUTTONS.LEFT_SHOULDER_BOTTOM) ||
        gamepad.buttonPressed_(pad, gamepad.BUTTONS.SELECT) ||
        gamepad.buttonPressed_(pad, gamepad.BUTTONS.START) ||
        gamepad.buttonPressed_(pad, gamepad.BUTTONS.LEFT_ANALOGUE_STICK),
    
  2. تعاملنا مع كل إدخال تناظري باعتباره إدخالاً منفصلاً، باستخدام دوال الحدود الموضّحة سابقًا.

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

    // Create and dispatch a corresponding key event.
    var event = document.createEvent('Event');
    var eventName = down ? 'keydown' : 'keyup';
    event.initEvent(eventName, true, true);
    event.keyCode = gamepad.stateToKeyCodeMap_[state];
    gamepad.containerElement_.dispatchEvent(event);

وهذا كل ما في الأمر!

نصائح

  • تذكَّر أنّ لوحة الألعاب لن تكون مرئية في المتصفح على الإطلاق قبل الضغط على أحد الأزرار.
  • إذا كنت تختبر وحدة التحكّم في الألعاب في متصفحات مختلفة في آنٍ واحد، تذكّر أنّ واحدًا منها فقط يمكنه استشعار وحدة التحكّم. إذا كنت لا تتلقى أي أحداث، تأكد من إغلاق الصفحات الأخرى التي يحتمل أن تستخدمها. بالإضافة إلى ذلك، من واقع خبرتنا، يمكن للمتصفح أحيانًا "التمسك" بلوحة الألعاب حتى إذا أغلقت علامة التبويب أو أغلقت المتصفّح نفسه. في بعض الأحيان، تكون إعادة تشغيل النظام هي الطريقة الوحيدة لإصلاح الأشياء.
  • وكالعادة، يمكنك استخدام Chrome Canary والأنواع المماثلة للمتصفّحات الأخرى للتأكد من أنك تحصل على أفضل دعم، وبعد ذلك احرص على اتخاذ إجراء مناسب إذا كنت ترى أن الإصدارات القديمة تتصرف بشكل مختلف.

المستقبل

نأمل أن يساعد ذلك في تسليط بعض الضوء على واجهة برمجة التطبيقات الجديدة هذه، والتي لا تزال غير مستقرة إلى حدٍ ما، ولكنها تتضمّن الكثير من المتعة.

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

قبل ذلك، يمكنك الاستمتاع باللعب باستخدام رسومات الشعار المبتكرة في لعبة Hurdles 2012 والاستمتاع بها على لوحة الألعاب. حسنًا، هل قلت أنّه يمكنك تحقيق أداء أفضل من 10.7 ثوانٍ؟ إحضارها.

محتوى إضافي للقراءة