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

רוב המפתחים בונים את מערכת השמירה הראשונה שלהם על ידי לקיחת אובייקט, הרצתו דרך JSON.stringify ושפיכת התוצאה לתוך localStorage. בעת טעינה, הם מבצעים לו parse ומחזירים אותו למשחק כפי שהוא. זה עובד ביום הראשון. זה נשבר ברגע שמוסיפים הגדרה חדשה, דגל פתיחה (unlock flag) חדש, או שכבה שלישית של קונפיגורציה מקוננת. אם שחקן חוזר עם קובץ שמירה ישן שחסרה בו תכונת vignette, והקוד החדש שלכם מצפה שהיא תהיה קיימת, תקבלו undefined במקום boolean. תכפילו את זה בעשרות פיצ'רים חדשים ותקבלו סיוט של דיבאגינג שפוגע קודם כל בשחקנים הנאמנים ביותר שלכם.

התחילו עם חוזה, לא עם

זה מעניק לך שני יתרונות מעשיים. ראשית, לעולם לא תנתח בטעות blob של v1 באמצעות לוגיקה של v2. שנית, תוכל לכתוב קוד מיגרציה (migration) אם תבחר בכך. בעת עליית המערכת (boot), בדוק אם קיימת גרסה v1. אם היא קיימת ו-v2 אינה קיימת, בצע מיגרציה של הנתונים הישנים למבנה החדש, כתוב אותם למפתח (key) החדש, והמשך הלאה. אם אינך רוצה לבצע מיגרציה, לפחות המפתח הישן יישאר ללא נזק באחסון בזמן שהקוד החדש שלך מתעלם ממנו. בכל מקרה, ניהול גרסאות מונע שחיתות נתונים שקטה (silent corruption).

הפוך את השמירה לבלתי נראית

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

שמור ברגע שהאינטראקציה מתרחשת. כאשר השחקן לוחץ על תיבת סימון כדי לבטל רעידת מסך, קרא לפונקציית הכתיבה שלך מיד. כאשר המשחק מסתיים והניקוד הסופי מחושב, כתוב את שיא הניקוד החדש לפני שמסך ה-"Game Over" מסיים את האנימציה שלו. שמירה מונעת אירועים (Event-driven saving) שומרת על הארכיטקטורה שלך צפויה, כיוון שהשמירה תמיד נמצאת ממש לצד הפעולה ששינתה את הנתונים. לעולם לא תצטרך לחפש פונקציית אצווה (batching) מרכזית או לדאוג ממצב לא מעודכן (stale state).

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

בנה כפתור איפוס עבור עצמך

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

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

השורה התחתונה

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