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