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

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

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

פיתוח תלת ממד בדפדפן

לאחר השקת 'כבישים איטיים', ראיתי הערה חוזרת במשוב: "לא ידעתי שזה אפשרי בדפדפן". כמו שאתם משתפים את הדעה הזו, אתם בהחלט לא מיעוט. לפי הסקר בנושא 2022 של JS, כ-80% מהמפתחים עדיין לא התנסו ב-WebGL. לדעתי, חבל שכל כך הרבה פוטנציאל נחמיץ, במיוחד כשמדובר במשחקים מבוססי-דפדפן. בעזרת Slow Roads אני מקווה להביא את WebGL לאור הזרקורים, ואולי לצמצם את מספר המפתחים שמעדיפים להשתמש בביטוי "מנוע משחק JavaScript עם ביצועים גבוהים".

WebGL עשוי להיראות מסתורי ומורכב בעיני רבים, אבל בשנים האחרונות המערכות האקולוגיות של הפיתוח שלו התפתחו מאוד לשימוש בכלים ובספריות יעילים ונוחים במיוחד. עכשיו קל יותר מאי פעם לשלב חוויית משתמש בתלת-ממד בעבודה שלהם, גם אם אין ניסיון קודם בגרפיקה ממוחשבת. Three.js, ספריית ה-WebGL המובילה, משמשת כבסיס להרחבות רבות, כולל react-שלוש-סיבים שמכניסים רכיבים תלת-ממדיים ל-React framework. יש עכשיו גם עורכים מקיפים למשחקים, כמו Babylon.js או PlayCanvas, שמציעים ממשק מוכר ורכיבי כלים משולבים.

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

השגת ביצועים טובים יותר ב'כבישים איטיים'

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

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

עיצוב המנוע של הסביבה סביב מהלך המשחק (gameplay)

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

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

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

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

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

בררניות עם חוקי הפיזיקה

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

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

ניהול טביעת הרגל הפחמנית

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

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

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

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

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

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

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

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

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

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

היסטוגרמה של זמני טעינה שמראה שיא משמעותי בשלוש השניות הראשונות, עם יותר מ-60% מהמשתמשים, ולאחר מכן ירידה מהירה. ההיסטוגרמה מראה שיותר מ-97% מהמשתמשים רואים זמני טעינה של פחות מ-10 שניות.

נקיטת גישה גמישה לאופטימיזציה מאוחרת

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

מעקב אחר חוויית המשתמש

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

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

הכבישים האיטיים בהמשך

גם אחרי ביצוע כל הפעולות האלה, נותר חלק משמעותי מבסיס השחקנים שצריך לשחק בהגדרות נמוכות יותר – בעיקר מכשירים שמשתמשים במכשירים קלים ללא GPU. מגוון הגדרות האיכות הזמינות מוביל לחלוקת ביצועים שווה יחסית, אבל רק 52% מהשחקנים משיגים יותר מ-55 FPS.

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

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

בזמן שפרויקטים חובבים כבר פועלים, Slow Roads עזרו להדגים עד כמה משחקי דפדפן פופולריים, ביצועים טובים ומפתיעים להפליא. אם הצלחתי לעורר את העניין שלך ב-WebGL, חשוב לדעת שכבישים איטיים מבחינה טכנולוגית היא דוגמה רדודה למדי ליכולות שלה. אני ממליץ בחום לקוראים לעיין בתצוגה של Three.js, ואנשים שמעוניינים במיוחד בפיתוח משחקים באינטרנט מוזמנים להצטרף לקהילה בכתובת webgamedev.com.