מקרה לדוגמה – בניית 'מציירים Google' של Stanisław Lem

Marcin Wichary
Marcin Wichary

שלום עולם (מוזר)

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

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

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

אנחנו לומדים הרבה מכל ציור אינטראקטיבי, והמיני-משחק האחרון ב-Stanisław Lem לא יוצא דופן, כי 17,000 השורות של קוד JavaScript שלו מנסים, בפעם הראשונה דברים רבים בהיסטוריית הציורים. היום אני רוצה לשתף איתכם את הקוד – אולי תמצאו שם משהו מעניין או לספר על הטעויות שלי – ולדבר עליו קצת.

הצג את הקוד של סטטיסלוב לם »

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

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

גרפיקה דרך DOM וקנבס

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

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

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

לצערי, המעבר לקנבס לא פשוט כמו שיקוף רקע של CSS עם drawImage(): מאבדים כמה דברים שבחינם כשיוצרים דברים דרך DOM, והכי חשוב ליצור שכבות עם אינדקסי z ואירועי עכבר.

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

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

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

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

קצב הפריימים

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

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

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

איזה סוג של החלטות?

  • אם קצב הפריימים גבוה מ-60fps, אנחנו מווסתים אותו. כרגע, ל-requestAnimationFrame בחלק מהגרסאות של Firefox אין מגבלה על קצב הפריימים, ואין טעם לבזבז את המעבד (CPU). שים לב שלמעשה אנחנו מגבילים את הקצב ל-65fps, ובגלל שגיאות העיגול שגורמות לקצב פריימים קצת יותר גבוה מ-60fps בדפדפנים אחרים – לא רוצים להתחיל לווסת את זה בטעות.

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

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

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

השוואה לשוק

ההנחה הייתה (ולמעשה, כבר בשלב מוקדם) שהקנבס יהיה מהיר יותר מ-DOM בכל פעם שהוא יהיה זמין. זה לא תמיד נכון. במהלך הבדיקה גילינו ש-Opera 10.0 עד 10.1 ב-Mac ו-Firefox ב-Linux הם למעשה מהירים יותר כשמעבירים רכיבי DOM.

בעולם המושלם, הדודל יבחן באופן שקט טכניקות גרפיות שונות – רכיבי DOM שהועברו באמצעות style.left ו-style.top, שרטוט על בד ציור, ואולי אפילו רכיבי DOM שנעים באמצעות המרות CSS3

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

בסופו של דבר, לפעמים פיתוח האתרים מסתכם בכך שצריך לעשות את מה שצריך לעשות. הבטתי מאחורי הכתף כדי לוודא שאף אחד לא מסתכל, ואז הקלדתי בתוך הקוד את Opera 10 ואת Firefox מתוך בד קנבס. בחיים הבאים, אני אחזור אליך בתור תג <marquee>.

חיסכון במעבד (CPU)

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

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

מתי?

  • אחרי 18 שניות בדף הבית (משחקי ארקייד שנקראים מצב המשיכה)
  • אחרי 180 שניות אם המיקוד בכרטיסייה
  • אחרי 30 שניות אם אין מיקוד בכרטיסייה (למשל, המשתמש עבר לחלון אחר אבל אולי הוא עדיין צופה בדודל בכרטיסייה לא פעילה)
  • באופן מיידי אם הכרטיסייה הופכת לבלתי נראית (למשל, המשתמש עבר לכרטיסייה אחרת באותו החלון – אין טעם לבזבז מחזורים אם אי אפשר לראות אותם)

איך אפשר לדעת אם הכרטיסייה מתמקדת כרגע? אנחנו מצרפים את עצמנו ל-window.focus ול-window.blur. איך אנחנו יודעים שהכרטיסייה גלויה? אנחנו משתמשים ב-Page Visibility API החדש ומגיבים לאירוע המתאים.

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

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

מעברים, טרנספורמציות, אירועים

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

הבעיות האלה ועוד, הן הסיבה ל-Lem דודל יש מעבר ומנוע טרנספורמציה משלו. כן, אני יודע, שנות ה-2000 נקראו וכו' – היכולות שצירפתי הן הרבה יותר חזקות כמו CSS3, אבל ללא קשר שהמנוע עושה זאת, הוא עושה את זה בעקביות ונותן לנו הרבה יותר שליטה.

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

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

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

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

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

התמודדות עם תמונות ודמויות Sprite

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

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

דודל פקמן
דמויות ה-Sprite ששימשו את הציור של פקמן.

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

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

בסצנות מסוימות, אנחנו משתמשים ביותר מ-Sprite אחד לא באותה מהירות כדי להאיץ את הטעינה באמצעות חיבורים מקבילים, אלא רק בגלל המגבלה של 3/5 מיליון פיקסלים לתמונות ב-iOS.

מה כל זה משתלב ב-HTML5? לא הרבה למעלה, אבל הכלי שכתבתי לשילוב/חיתוך היה טכנולוגיית אינטרנט חדשה: בד ציור, blobs, a[הורדה]. אחד הדברים הנהדרים ב-HTML הוא שהסורק סורק בהדרגה דברים שקודם היה צריך לבצע מחוץ לדפדפן. החלק היחיד שהיינו צריכים לעשות הוא לבצע אופטימיזציה של קובצי PNG.

שמירת המצב בין המשחקים

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

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

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

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

מה אנחנו עושים עם המידע הזה?

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

יש כמה פרמטרים לניפוי באגים ששולטים בכך:

  • ?doodle-debug&doodle-first-run – נניח שזו הפעלה ראשונה
  • ?doodle-debug&doodle-second-run – נדמה שזו הפעלה שנייה
  • ?doodle-debug&doodle-old-run – מעמידים פנים שזו קבוצה ישנה

מכשירי מגע

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

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

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

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

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

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

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

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

התאמה אישית של מצביע העכבר

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

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

אם לא, מה? אז למה לא לבחור בסמן העכבר רק שחקן נוסף בדודל? זה עובד, אבל יש כמה דברים שכדאי לשים לב אליהם, בעיקר:

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

הדרך הראשונה מסובכת. CSS3 מאפשר את cursor: none, אבל גם הוא לא נתמך בדפדפנים מסוימים. היינו צריכים לפנות למכונות כושר: להשתמש בקובץ .cur ריק כחלופה, לציין התנהגות קונקרטית בדפדפנים מסוימים, ואפילו לבצע קידוד קשיח של אחרים בהתאם לניסיון.

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

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

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

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

סיכום

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

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

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

צילום מסך של ציור לם בשעון ספירה לאחור ביקום.
צילום מסך של שעון הספירה לאחור של לם ביקום.

וזו דרך אחת להביט בחייו של דודל של Google – חודשים של עבודה, שבועות של בדיקות, 48 שעות של אפייה, וכל זה בשביל משהו שאנשים משחקים במשך חמש דקות. כל אחד מאלפי שורות JavaScript האלה מקווה ש-5 הדקות האלה ינוצלו כמו שצריך. צפייה מהנה.