כאן מוסבר איך משתמשים ב-Gamepad API כדי לשדרג את משחקי האינטרנט.
ביצת ההפתעה בדף האופליין של Chrome היא אחד הסודות הכי גלויים בהיסטוריה ([citation needed]
, אבל הטענה הזו נועדה ליצור אפקט דרמטי). אם לוחצים על מקש הרווח או, במכשירים ניידים, מקישים על הדינוזאור, הדף במצב אופליין הופך למשחק ארקייד שאפשר לשחק בו. יכול להיות שאתם יודעים שלא צריך להתנתק מהאינטרנט כדי לשחק: ב-Chrome, אפשר פשוט לעבור אל about://dino
או, אם אתם חובבי טכנולוגיה, אל about://network-error/-106
. אבל ידעתם שבכל חודש משחקים 270 מיליון משחקים של דינוזאור 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
: סוג enum שמגדיר באיזו יד מחזיקים את בקר המשחק, או באיזו יד הכי סביר שיחזיקו אותו. 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 או 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 dino באמצעות בקר המשחקים שלכם באתר הזה. קוד המקור זמין ב-GitHub.
אפשר לעיין בהטמעה של סקר בקר המשחקים בכתובת
trex-runner.js
ולראות איך היא מדמה לחיצות על מקשים.
כדי שהדמו של בקר המשחקים של Chrome Dino יעבוד, הוצאתי את המשחק Chrome Dino מליבת הפרויקט של Chromium (עדכון של מאמץ קודם של Arnelle Ballane), הצבתי אותו באתר עצמאי, הרחבתי את ההטמעה הקיימת של Gamepad API על ידי הוספת אפקטים של הנמכה ורטט, יצרתי מצב מסך מלא ו-Mehul Satardekar תרם הטמעה של מצב כהה. שיהיה לכם משחק מהנה!
קישורים מועילים
תודות
המסמך הזה נבדק על ידי François Beaufort וJoe Medley. מפרט Gamepad API נערך על ידי Steve Agoston, James Hollyer ו-Matt Reynolds. העורכים הקודמים של המפרט הם ברנדון ג'ונס, סקוט גרהאם וטד מילצ'ארק. מפרט התוספים של הגיימפאד נערך על ידי ברנדון ג'ונס. תמונה ראשית (Hero) מאת Laura Torrent Puig.