חוויית ההוביט

מעוררים את הארץ התיכונה באמצעות WebGL לנייד

Daniel Isaksson
Daniel Isaksson

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

מוקדם יותר השנה התחלנו פרויקט בשיתוף עם חברים מ-Google ומ-Warner Bros. במטרה ליצור חוויית אינטרנט מותאמת לנייד עבור הסרט החדש 'ההוביט', ההוביט: מפלתו של סמאוג. בניית ניסוי Chrome לנייד עם הרבה מולטימדיה הייתה משימה מעוררת השראה ומאתגרת.

החוויה עברה אופטימיזציה ל-Chrome ל-Android במכשירי ה-Nexus החדשים שבהם יש לנו עכשיו גישה ל-WebGL ול-Web Audio. עם זאת, ניתן לגשת לחלק גדול מהחוויה גם במכשירים ובדפדפנים שאינם WebGL, באמצעות איחוד עם האצת חומרה ואנימציות CSS.

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

האתגרים של WebGL במכשירים ניידים

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

בניסוי השתמשנו ב-three.js כפי שעשינו בחלק מהפרויקטים הקודמים של WebGL. התחלנו בתהליך ההטמעה ויצרנו גרסה ראשונית של משחק ה-Trollshaw שיפעל היטב בטאבלט Nexus 10. לאחר מספר בדיקות ראשוניות במכשיר, חשבנו על רשימת אופטימיזציות שנראות כמו בדרך כלל שהיו משתמשים במחשב נייד עם מפרט נמוך:

  • השתמשו במודלים של מספר פוליגונים
  • שימוש במרקמים ברזולוציה נמוכה
  • הפחתת מספר המשיכה ככל האפשר על ידי מיזוג גיאומטריה
  • יצירת חומרים ותאורה פשוטים יותר
  • הסרת האפקטים של הפוסט והשבתת הקו הנטוי
  • אופטימיזציה של ביצועי JavaScript
  • עיבוד בד ציור WebGL בגודל חצי והתאמה לעומס (scaling) באמצעות CSS

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

השתמשו במודלים של מספר פוליגונים

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

אחד הטרולים ביער טרולשאו
אחד מהטרולים ביער הטרולים

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

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

שימוש במרקמים ברזולוציה נמוכה

כדי לקצר את זמן הטעינה במכשירים ניידים, בחרנו לטעון מרקמים שונים שהיו בגודל של חצי מגודל המרקמים בשולחן העבודה. מסתבר שכל המכשירים יכולים לטפל בגודלי מרקם עד 2,048x2,048 פיקסלים, ורובם יכולים להתמודד עם רזולוציה של 4,096x4,096 פיקסלים. נראה שאין בעיה בחיפוש מרקם אחר מרקמים בודדים לאחר העלאתם ל-GPU. הגודל הכולל של המרקמים חייב להתאים לזיכרון ה-GPU כדי למנוע מצב שבו המרקמים מעלים ומורידים אותם כל הזמן, אבל זו כנראה לא בעיה גדולה עבור רוב חוויות האינטרנט. עם זאת, חשוב לשלב מרקמים במספר קטן ככל האפשר של Spritesheets כדי לצמצם את מספר המשיכה. זה משהו שיש לו השפעה גדולה על הביצועים במכשירים ניידים.

מרקם של אחד הטרולים ביער טרולשאו
מרקם של אחד מהטרולים ביער טרולש
(גודל מקורי: 512x512 פיקסלים)

יצירת חומרים ותאורה פשוטים יותר

בחירת החומרים יכולה להשפיע מאוד על הביצועים, וצריך לנהל אותה בחוכמה בנייד. אחד מהדברים שבהם השתמשנו כדי לשפר את הביצועים הוא השימוש ב-MeshLambertMaterial (חישוב של אור קודקוד) ב-3.js במקום ב-MeshPhongMaterial (חישוב אור ב-texel). בעיקרון, ניסינו להשתמש בצללים פשוטים עם כמה שפחות חישובי תאורה.

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

scene.overrideMaterial = new THREE.MeshBasicMaterial({color:0x333333, wireframe:true});

אופטימיזציה של ביצועי JavaScript

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

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

לדוגמה, צריך להגדיר את הקוד כך:

var currentPos = new THREE.Vector3();

function gameLoop() {
  currentPos = new THREE.Vector3(0+offsetX,100,0);
}

גרסה משופרת של הלולאה הזו מונעת יצירה של אובייקטים חדשים שחייבים לאסוף אשפה:

var originPos = new THREE.Vector3(0,100,0);
var currentPos = new THREE.Vector3();
function gameLoop() {
  currentPos.copy(originPos).x += offsetX;
  //or
  currentPos.set(originPos.x+offsetX,originPos.y,originPos.z);
}

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

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

עיבוד בד ציור WebGL בגודל חצי והתאמה לעומס (scaling) באמצעות CSS

הגודל של בד הציור של WebGL הוא ככל הנראה הפרמטר היעיל ביותר שתוכל לשנות כדי לשפר את הביצועים. ככל שהשטח שבו משתמשים כדי לצייר את סצנת התלת-ממד יהיה גדול יותר, כך יהיה צורך לצייר יותר פיקסלים בכל פריים. זה כמובן משפיע על הביצועים.מכשיר ה-Nexus 10 עם מסך הפיקסלים שלו, 2560x1600, בצפיפות גבוהה של 2560x1600, צריך לדחוף פי 4 את מספר הפיקסלים מטאבלט בצפיפות נמוכה. כדי לבצע אופטימיזציה לנייד, אנחנו משתמשים בטריק שבו אנחנו מגדירים את שטח הקנבס לחצי מהגודל (50%), ולאחר מכן מגדילים את שטח הקנבס כך שיתאים לגודלו המתוכנן (100%) באמצעות המרות CSS תלת-ממדיות עם האצת חומרה. החיסרון של התמונה הזו הוא תמונה מפוקסלת, שבה קווים דקים עלולים להפוך לבעיה, אבל במסך עם רזולוציה גבוהה, האפקט לא כזה נורא. הביצועים הנוספים האלה בהחלט שווים.

אותה סצנה ללא שינוי גודל בבד ציור ב-Nexus 10 (16FPS) ובקנה מידה של 50% (33FPS)
אותה סצנה ללא שינוי גודל בבד ציור ב-Nexus 10 (16FPS) ובקנה מידה של 50% (33FPS).

אובייקטים כאבני בניין

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

אבני בניין של אובייקטים תלת ממדיים המשמשים במבוך של דול גולדור.
אבני בניין של אובייקטים תלת ממדיים שמשמשים במבוך של דול גולדור.

ב-Rrivendell יש כמה קטעים קרקע שאנחנו ממקמים באופן קבוע בעומק Z במהלך התקדמות המשתמש. כשהמשתמש עובר בין קטעים, המיקום שלהם משתנה במרחק הרחוק.

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

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

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

שימוש באינטראקציה במגע בחוויות האינטרנט לנייד

לא קשה להוסיף תמיכה במגע. יש מאמרים מצוינים בנושא. אבל יש כמה דברים קטנים שיכולים לסבך את התהליך.

אפשר להשתמש גם בעכבר וגם בנגיעה. Chromebook Pixel ומחשבים ניידים אחרים עם תמיכה במגע כוללים תמיכה בעכבר ובמגע. אחת השגיאות הנפוצות היא לבדוק אם המכשיר מופעל במגע, ולאחר מכן להוסיף רק פונקציות event listener ולא לעכבר.

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

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

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

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

סיכום

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

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

הניסוי הושק וזה היה מסע מדהים. אנחנו מקווים שתיהנו!

רוצים לנסות? יוצאים למסע אל הארץ התיכונה.