יצירת גלגל

Justin Gitlin
Justin Gitlin

Roll It הוא ניסוי של Chrome, שממציא מחדש משחק קלאסי בטיילת באמצעות הדפדפן בטלפון ובמחשב בלבד. הדפדפן בטלפון שלך מאפשר לך לכוון ולגלגל את הכדור בתנופה של פרק כף היד, בזמן שהדפדפן במחשב שלך מציג את הגרפיקה בזמן אמת של סמטת Roll It באמצעות WebGL ו-Canvas. שני המכשירים מתקשרים ביניהם באמצעות Websockets. אין אפליקציות. ללא הורדות. אין אסימונים. כל מה שצריך הוא דפדפן מודרני.

על פי ההנחיות של Google Creative Lab, פיתחו ב-Legwork את חוויית המשתמש, הממשקים וסביבת המשחק. לאחר מכן עבדו בשיתוף עם שותף הפיתוח, Mode Set (הגדרות מצב) כדי לפתח את Roll It. במהלך הפרויקט היו מספר אתגרים ייחודיים. מאמר זה מציג כמה מהטכניקות שבהן השתמשנו, טריקים שגילינו ומהם הלקחים שלמדנו תוך כדי להוציא לפועל את התכונה.

תהליך עבודה בתלת ממד

אחד האתגרים בהתחלה היה למצוא את הדרך הטובה ביותר להביא מודלים תלת-ממדיים מהתוכנה שלנו לפורמט קובץ מוכן לאינטרנט. אחרי שיוצרים את הנכסים בתוך Cinema 4D, המודלים פשוטים יותר והומרו לרשתות עם פוליגונים נמוכים. לכל רשת ניתנו תגים מסוימים לבחירת פוליגונים כדי להבחין בין חלקים של האובייקט לצורך צביעה ותכנון טקסט. אחר כך הצלחנו לייצא כקובץ Collada 1.5 (.dae) ולייבא אותו אל Blender, תוכנית תלת-ממד בקוד פתוח, כדי ליצור קבצים תואמים ל-3.js. אחרי שווידאנו שהמודלים שלנו יובאו כראוי, ייצאנו את הרשת כקובץ JSON והתאורה הוחלה באמצעות קוד. הנה סקירה מפורטת יותר של הצעדים שעשינו:

צרו מודל לאובייקט בתוך C4D. מוודאים שהרשת הרגילה פונה כלפי חוץ.
יצירת מודל של האובייקט בתוך C4D. מוודאים שהרשת הרגילה פונה כלפי חוץ.
באמצעות הכלי לבחירת פוליגונים, יוצרים תגי בחירה של האזורים הספציפיים שרוצים ליצור להם מרקם. החלת חומרים על כל אחד מתגי הבחירה.
באמצעות הכלי לבחירת פוליגונים, יוצרים תגי בחירה של האזורים הספציפיים שרוצים ליצור להם מרקם. החלת חומרים על כל אחד מתגי הבחירה.
מייצאים את הרשת כקובץ COLLADA 1.5 .dae
מייצאים את הרשת כקובץ COLLADA 1.5 .dae
מוודאים שהאפשרות 'ייצוא גיאומטריה דו-ממדית' מסומנת. ייצוא משולשים נתמך בדרך כלל באופן נרחב יותר בסביבות תלת-ממדיות בצד הקוד, אבל החסרונות שלו הם הכפלת מספר הפוליגונים. ככל שמספר הפוליגונים גבוה יותר, כך המודל יהיה מיסוי יותר במעבד של המחשב. לכן, אם אתם מבחינים בביצועים איטיים, השאירו את האפשרות הזו מסומנת.
מוודאים שהאפשרות 'ייצוא גיאומטריה דו-ממדית' מסומנת. ייצוא משולשים נתמך בדרך כלל באופן נרחב יותר בסביבות תלת-ממדיות בצד הקוד, אבל החסרונות שלו הם הכפלת מספר הפוליגונים. ככל שמספר הפוליגונים גבוה יותר, כך המודל יהיה מיסוי יותר במעבד של המחשב. לכן, אם אתם מבחינים בביצועים איטיים, השאירו את האפשרות הזו מסומנת.
מייבאים את קובץ ה-Collada לבלנדר.
מייבאים את קובץ Collada לבלנדר.
אחרי שתייבאו אותו לבלנדר, תראו שגם החומרים ותגי הבחירה שלכם הועברו.
אחרי הייבוא לבלנדר, אפשר לראות שגם החומרים ותגי הבחירה הועברו.
בוחרים את האובייקט הרצוי ומתאימים את החומר שממנו עשוי העצם לפי ההעדפה שלכם.
בוחרים את האובייקט ומתאימים את החומרים שלו לפי ההעדפה שלכם.
לייצא את הקובץ כקובץ שלושה.js
לייצא את הקובץ כקובץ שלושה.js לתאימות ל-webGL.

כתיבת הקוד

Roll הוא פותח באמצעות ספריות של קוד פתוח, והוא פועל באופן מקורי בדפדפנים מודרניים. עם טכנולוגיות כמו WebGL ו-WebSockets, רשת האינטרנט הולכת ומתקרבת לחוויות גיימינג ומולטימדיה באיכות קונסולה. הקלות והנוחות שבהן מפתחים יכולים לפתח חוויות כאלה השתפרו מאוד, ככל שהפכו כלים מודרניים יותר לזמינים עבור פיתוח HTML.

סביבת הפיתוח

רוב הקוד המקורי של Roll It נכתב באמצעות CoffeeScript - שפה נקייה ותמציתית שעוברת הידור ל-JavaScript בפורמט תקין ולאחר איתור שגיאות בקוד. הקפה ב-CupScript מוביל לשיפור בפיתוח של OOP בזכות מודל הירושה הנהדר שלו והטיפול בהיקף הנקי יותר. ה-CSS נכתב באמצעות SASS framework, שמעניקה למפתח כמה כלים מעולים כדי לשפר ולנהל דפי סגנונות של פרויקט. הוספת המערכות האלה לתהליך ה-build לוקחת קצת זמן להגדיר, אבל התוצאה בהחלט שווה את זה, במיוחד בפרויקט גדול יותר כמו Roll It. הגדרנו שרת של Ruby on Rails כדי להדר באופן אוטומטי את הנכסים שלנו במהלך הפיתוח, כך שכל שלבי האיסוף האלה הפכו לשקופים.

מעבר ליצירת סביבת קידוד יעילה ונוחה, ביצענו אופטימיזציה של הנכסים באופן ידני כדי לצמצם את הבקשות על מנת לטעון את האתר מהר יותר. הפעלנו כל תמונה באמצעות כמה תוכנות דחיסה – ImageOptim ו-ImageAlpha. בכל תוכנית מתבצעת אופטימיזציה של התמונות בדרך משלה – אובדן או אובדן, בהתאמה. שילוב נכון של הגדרות יכול להפחית באופן משמעותי את גודל הקובץ של התמונה. אפשרות זו לא רק חוסכת ברוחב פס כשטוענים תמונות חיצוניות, אלא לאחר האופטימיזציה, התמונות יתורגמו למחרוזות קטנות יותר בקידוד base64 להטמעה מוטבעת ב-HTML, ב-CSS וב-JavaScript. לגבי קידוד base64, אנחנו גם מטמיעים את קובצי הגופנים מסוג Open Sans מסוג WOFF ו-SVG ישירות ב-CSS באמצעות השיטה הזו. התוצאה הייתה מספר קטן עוד יותר של בקשות.

סצנה בתלת-ממד שמבוססת על פיזיקה

THREE.js היא ספריית JavaScript בתלת-ממד שנמצאת בכל מקום באינטרנט. הוא כולל בתוכו אופטימיזציות WebGL ברמה נמוכה למתמטיקה תלת-ממדית ומבוססת חומרה, שמאפשרות לבני תמותה ליצור בקלות סצנות תלת-ממדיות אינטראקטיביות ומוארות היטב, ללא צורך לכתוב תוכנות הצללה מותאמות אישית או לבצע טרנספורמציות ידניות במטריצות. Physijs הוא wrapper ספציפי ל-THREE.js לספריית פיזיקה פופולרית של C++ שתורגמה ל-JavaScript. ניצלנו את הספרייה כדי לדמות את הכדור, הקפיצה והקפיצה לקראת היעד בתלת-ממד.

כבר מההתחלה, המטרה שלנו הייתה לוודא שהחוויה הפיזית של סיבוב הכדור נרגשת לא רק מציאותית, אלא גם כדי לוודא שהחפצים במשחק נראים אמיתיים. לשם כך, נדרשו ניסיונות רבים לשנות את כוח הכבידה הכולל של הסצנה לפיפיס, את מהירות הכדור כשהוא מתגלגל מזריקת השחקן, את שיפוע הקפיצה של השביל ואת תכונות החיכוך וההחזרה (קפיצות) של חומרי הכדור והנתיב. השילוב של יותר כוח משיכה ויותר מהירות הוביל לחוויית משחק מציאותית יותר.

החלקה

רוב השילובים המודרניים של דפדפנים וכרטיסי וידאו אמורים לנצל את היתרונות של קיבוע נגד קיפולים מבוסס חומרה בסביבת WebGL, אבל חלקם לא יצליחו. אם החלקת הקצוות אינה פועלת באופן טבעי, קצוות קשים ומנוגדים בסצנה של THREE.js יהיו משוננים ומכוערים (לכל הפחות לעיניים חדות).

למרבה המזל, יש תיקון: באמצעות קטע קוד אנחנו יכולים לזהות אם הפלטפורמה תתמוך במקור של החלקת קצוות. אם זה לא המצב, אנחנו מוכנים לעבוד. אם לא, יש סדרה של תוכנות הצללה לאחר עיבוד שמגיעות עם THREE.js שיכולות לעזור לנו. כלומר, המסנן 'FXAA' נגד קצוות משוננים. שרטוט מחדש של כל פריים באמצעות ההצללה (shader) הזה מעניק לנו מראה חלק יותר בקווים ובקצוות שלנו. ראו את ההדגמה למטה:

// Check for native platform antialias support via the THREE renderer
// from: http://codeflow.org/entries/2013/feb/22/how-to-write-portable-webgl/#antialiasing
var nativeAntialiasSupport = (renderer.context.getParameter(renderer.context.SAMPLES) == 0) ? false : true;

פקדי משחקים המבוססים על מד תאוצה

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

זיהוי הגלגול של תנועת ה'גלגלת' הראשית מתחילה במעקב אחר 10 העדכונים האחרונים של מד התאוצה שהגיעו מהאירוע deviceorientation בחלון. על ידי חיסור ערך ההטיה הקודם מערך ההטיה הנוכחי, אנחנו שומרים את ההפרש של הזווית בין האירועים. לאחר מכן, באמצעות סיכום קבוע של עשר השערים האחרונות בזווית, אנחנו יכולים לזהות סיבוב רציף בזמן שהטלפון נע בחלל. כשהטלפון עובר סף של שינוי זוויתי, אנחנו מפעילים גלגול. לאחר מכן, על ידי מציאת דלתת ההטיה הגדולה ביותר בהיבט הזה, נוכל להעריך את מהירות הכדור. המהירות הזו מנורמלת על ידי נירמול באמצעות חותמות הזמן שאנחנו מצרפים לכל עדכון של מד התאוצה. הפעולה הזאת יכולה לעזור לכם להחליק את המהירות המשתנה שבה עדכונים של מד התאוצה זורמים לדפדפן במכשירים שונים.

תקשורת WebSockets

לאחר שהשחקן מגלגל את הכדור עם הטלפון שלו, נשלחת הודעה מהטלפון למחשב הנייד וקוראת לו להפעיל את הכדור. הודעת 'roll' נשלחת באמצעות אובייקט נתונים מסוג JSON דרך חיבור WebSocket בין שני המחשבים. נתוני ה-JSON קטנים, בעיקר מורכבים מסוג הודעה, מהירות זריקה וכיוון המטרה.

{
  "type": "device:ball-thrown",
  "speed": 0.5,
  "aim": 0.1
}

כל התקשורת בין המחשב הנייד לטלפון מתרחשת באמצעות הודעות JSON קטנות כמו זו. בכל פעם שהמצב של המשחק מתעדכן במחשב, או שהמשתמש מסובב או מקיש על לחצן בטלפון, הודעת WebSocket נשלחת בין המכשירים. כדי שהתקשורת הזו תהיה פשוטה וקלה לניהול, ההודעות של WebSockets משודרות באמצעות נקודת יציאה אחת מכל אחד מהדפדפנים. לעומת זאת, יש נקודת כניסה אחת בדפדפן המקבל, שבה אובייקט WebSocket אחד מטפל בכל ההודעות הנכנסות והיוצאות בשני הקצוות. כאשר מתקבלת הודעת WebSocket, נתוני ה-JSON משודרים מחדש בתוך אפליקציית JavaScript באמצעות שיטת trigger() של jQuery. בשלב הזה, הנתונים הנכנסים מתנהגים בדיוק כמו כל אירוע DOM אחר מותאם אישית, וניתן לאסוף ולעבד אותם על ידי כל אובייקט אחר באפליקציה.

var websocket = new WebSocket(serverIPAddress);

// rebroadcast incoming WebSocket messages with a global event via jQuery
websocket.onmessage = function(e) {
  if (e.data) {
    var obj = JSON.parse(e.data);
    $(document).trigger(data.type, obj);
  }
};

// broadcast outgoing WebSocket messages by passing in a native .js object
var broadcast = function(obj) {
  websocket.send(JSON.stringify(obj));
};

שרתי WebSocket שלהם נוצרים במהירות כאשר שני מכשירים מסונכרנים עם קוד משחק. הקצה העורפי של Roll It נבנו בפלטפורמת Google Compute Engine ובפלטפורמת App Engine באמצעות Go.

הטיה של מסכי התפריט

בנוסף להודעות WebSocket מבוססות-אירועים שבהן משתמשים במהלך המשחק, התפריטים נשלטים על ידי הטיית הטלפון והקשה על לחצן לאישור הבחירה. לשם כך, נדרש זרם עקבי יותר של נתוני הטיה שמועברים מהטלפון למחשב הנייד. כדי לצמצם את רוחב הפס ולהימנע משליחת עדכונים מיותרים, ההודעות האלה נשלחות רק אם הטיית המכשיר השתנתה ביותר מכמה מעלות. אין טעם לשלוח זרם של נתוני הטיה אם הטלפון מונח במצב שטוח על שולחן! בנוסף, קצב ההעברה מווסת - לא נשלחות יותר מ-15 הודעות WebSockets לשנייה ב'גליל', גם אם המכשיר נוטה באופן פעיל.

לאחר זיהוי ערכי ההטיה במחשב, הם עוברים אינטרפולציה לאורך זמן באמצעות requestAnimationFrame כדי לשמור על תחושה חלקה. התוצאה הסופית היא תפריט מסתובב וכדור שמתגלגל כדי לעזור בציון הבחירה של המשתמש. בזמן שהטלפון שולח נתוני הטיה, רכיבי DOM האלה מתעדכנים בזמן אמת על ידי חישוב מחדש של התמרת CSS בתוך הלולאה requestAnimationFrame. המכל בתפריט פשוט מסתובב, אבל נראה שהכדור מתגלגל על הרצפה. כדי להשיג את האפקט הזה, אנחנו מיישמים טריגונומטריה בסיסית כדי לקשר את קואורדינטת ה-x לסיבוב שלה. המשוואה הפשוטה היא: סיבובים = x / (קוטר * תפקיד)

סיכום

גליל הוא סימן לזמנים. מדובר בתקופה מרגשת וטרנספורמציה מאוד בין הפרויקטים של הקוד הפתוח שהובילו את הפיתוח שלה לבין עוצמת העיבוד של המכשירים שעל שולחנות העבודה והכיסים שלנו, לבין מצב האינטרנט כפלטפורמה. לפני כמה שנים, רוב הטכנולוגיה הזו הייתה קיימת רק במערכות קנייניות, ולא ניתן היה להשתמש בה ולהפיץ אותה באופן חופשי. כיום, אנחנו יוצרים ומשתפים חלקים חדשים מהחידה בכל יום, וכך מממשים חוויות מורכבות עם פחות עבודה ועם יותר דמיון. אז קדימה, למה אתם מחכים? בנו משהו מדהים ושתפו אותו עם העולם!

גלילת הלוגו