איך לשחק במשחק Chrome Dino באמצעות הגיימפאד

איך משתמשים ב-Gamepad API כדי לשדרג את משחקי האינטרנט שלכם?

ביצת ההפתעה בדף אופליין של Chrome היא אחד הסודות הגרועים ביותר בהיסטוריה ([citation needed], אבל הטענה הזו נאמרה רק כדי ליצור אפקט דרמטי). אם מקישים על מקש הרווח או, במכשירים ניידים, מקישים על הדינוזאור, הדף אופליין הופך למשחק ארקייד שניתן לשחק בו. אולי כבר שמעתם שאתם לא צריכים לעבור למצב אופליין כדי לשחק: ב-Chrome, אתם יכולים פשוט לעבור אל about://dino, או, אם אתם גיקים, לעבור אל about://network-error/-106. אבל האם ידעת ש270 מיליון משחקים של Chrome Dino נערכים מדי חודש?

הדף של Chrome במצב אופליין עם המשחק Chrome Dino.
מקישים על מקש הרווח כדי להתחיל לשחק.

עובדה נוספת שחשוב לדעת, ויכול להיות שלא ידעתם, היא שבמצב ארקייד אפשר לשחק במשחק באמצעות גיימפאד. התמיכה ב-Gamepad נוספה לפני כשנה, בcommit של Reilly Grant. כפי שאתם רואים, המשחק, כמו שאר הפרויקט של Chromium, הוא קוד פתוח לחלוטין. במאמר הזה אסביר איך משתמשים ב-Gamepad API.

שימוש ב-Gamepad API

זיהוי תכונות ותמיכה בדפדפנים

ל-Gamepad API יש תמיכה רחבה בדפדפנים, גם במחשבים וגם בניידים. אפשר לזהות אם יש תמיכה ב-Gamepad API באמצעות קטע הקוד הבא:

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

איך הדפדפן מייצג גיימפאד

הדפדפן מייצג את הגיימפדים כאובייקטים מסוג Gamepad. ל-Gamepad יש את המאפיינים הבאים:

  • id: מחרוזת מזהה של משחקן הווידאו. המחרוזת הזו מזהה את המותג או הסגנון של מכשיר ה-gamepad המחובר.
  • displayId: הערך של VRDisplay.displayId של VRDisplay משויך (אם רלוונטי).
  • index: האינדקס של משחק הווידאו בנתב.
  • connected: מציין אם השלט עדיין מחובר למערכת.
  • hand: enum שמגדיר באיזו יד השלט מוחזק, או באיזו יד סביר להניח שהוא מוחזק.
  • timestamp: הפעם האחרונה שבה עודכנו הנתונים של שלט ה-gamepad הזה.
  • 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. יש שני סוגים של אפקטים של רעידות:

  • Dual-Rumble: אפקט המשוב החזותי שנוצר על ידי שני מפעילי מסה מסתובבים אקסצנטריים, אחד בכל אחיזה של שלט המשחקים.
  • Trigger-Rumble: אפקט המשוב החזותי שנוצר על ידי שני מנועים עצמאיים, כאשר מנוע אחד ממוקם בכל אחד מהטריגרים של שלט המשחק.

בסקירה הכללית הסכמטית הבאה, שנלקחה ישירות מהמפרט, מוצגים המיפוי והסידור של הלחצנים והצירים במשטח משחק כללי.

סקירה כללית סכמטית של מיפוי הלחצנים והצירים של שלט משחקים נפוץ.
תצוגה חזותית של פריסה רגילה של שלט משחקים (מקור).

התראה כשגיימפאד מחובר

כדי לדעת מתי משחקן מחוברת, אפשר להאזין לאירוע gamepadconnected שמופעל באובייקט window. כשהמשתמש מחבר גיימפאד, וניתן לעשות זאת באמצעות USB או באמצעות Bluetooth, מתרחש אירוע 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 או Bluetooth ולוחצים על אחד מהלחצנים שלו או מזיזים את אחד הצירים שלו.

בונוס: משחק Chrome Dino ב-web.dev

אפשר לשחק בדינוזאור של Chrome עם שלט המשחקים באתר הזה. קוד המקור זמין ב-GitHub. כדאי לעיין בהטמעה של הסקרים של משחקי הלחצנים ב-trex-runner.js ולראות איך מתבצעת ההדמיה של הקשות על המקשים.

כדי שהדגמה של משחק הדינוזאור של Chrome עם שלט תפעל, הוצאתי את משחק הדינוזאור של Chrome מפרויקט הליבה של Chromium (עדכון של מאמץ קודם של Arnelle Ballane), העברתי אותו לאתר עצמאי, הרחיבתי את ההטמעה הקיימת של API למשחקי מחשב על ידי הוספת אפקטים של צמצום רטט, יצרתי מצב מסך מלא וMehul Satardekar תרם להטמעה של מצב כהה. גיימינג מהנה!

תודות

המסמך הזה נבדק על ידי François Beaufort ו-Joe Medley. עורכי המפרט של Gamepad API הם Steve Agoston,‏ James Hollyer ו-Matt Reynolds. עורכי המפרט הקודמים הם ברנדון ג'ונס, סקוט גרהם וטד מיילצ'ארק. ברנדון ג'ונס הוא העורך של המפרט של תוספים לגיימפאד. התמונה הראשית (Hero) היא של Laura Torrent Puig.