ה-roguelike הטובים ביותר לא רק הורגים אותך. הם גורמים לך לרצות למות.
זה נשמע מוזר, אבל כל מי שאיבד ריצה בחצות והתחיל אחת חדשה ב-12:03 מכיר את התחושה. המוות צורב. הבריאות שלך צונחת לאפס. המסך מוצף בכישלון. ובכל זאת, האצבע שלך כבר מרחפת מעל כפתור ה-play. משהו ב-20 הדקות האחרונות היה חשוב מספיק כדי שלא תוכל לעזוב את המשחק במקום שבו הוא נגמר.
זהו לופ ה-"עוד ריצה אחת", וזה לא מקרי. זה מתוכנן. אם אתם בונים roguelike או כל משחק עם permadeath, כל העבודה שלכם היא לאזן בין שני כוחות מנוגדים. השחקן חייב להפסיד מספיק כדי להרגיש מתח. הוא חייב לשמור מספיק כדי להרגיש תקווה.
המטבע של הכישלון
בפרויקט האחרון שלי, Neon Survivor, רציתי בדיוק את המתח הזה. כשהשחקן מת, כל מה שנאסף במהלך הריצה נעלם, למעט דבר אחד: זהב. הזהב הזה נשמר בבנק באופן אוטומטי. בחזרה בתפריט, הם מוציאים אותו על שדרוגים קבועים. ואז הם קופצים פנימה שוב, מעט חזקים יותר מבעבר.
הלופ הפשוט הזה נושא את המשחק כולו. בלעדיו, המוות הוא נקודה לסיום. השחקן עוזב כי הריצה האחרונה לא נתנה לו כלום. איתו, המוות הוא פסיק. הריצה הפכה למסע איסוף (farming). ההפסד כואב, אבל הוא גם שילם על מחר.
הטריק הוא פשוט. אתם שומרים משהו גם כשאתם מאבדים את כל השאר. הקושי הוא לוודא שהדבר שאתם שומרים באמת משמעותי מבלי להרוס את האתגר. אם השדרוגים חלשים מדי, לשחקן לא יהיה אכפת. אם הם חזקים מדי, המשחק משחק את עצמו. הלופ קורס בשני המקרים.
שני שעונים, אפס בלבול
כדי לבנות את זה בצורה נכונה, הייתי צריך לנהל שני ציר זמן נפרדים.
שעון הריצה (Run Clock) מתאפס בכל פעם שלוחצים על play. הוא עוקב אחרי בריאות, ניקוד נוכחי, מספר גלי אויבים וכל ה-power-ups הזמניים שנאספו במהלך הסשן. כשהדמות מתה, השעון הזה חוזר לאפס.
שעון המטא (Meta Clock) לעולם לא מתאפס. הוא מחזיק את סך הזהב שנצבר בכל הניסיונות, הגל הגבוה ביותר שהושג אי פעם, וכל שדרוג קבוע שנרכש. השעון הזה ממשיך לתקתק לא משנה כמה פעמים מרעננים את הדפדפן.
ערבבו את השניים האלה ותקבלו באגים שקשה לעקוב אחריהם וכואבים לתיקון. ראיתי מפתחים שמוחקים בטעות את התקדמות השחקן במהלך איפוס סצנה שגרתי כי פונקציית ניקוי נגעה במאגר הנתונים הלא נכון. נתוני ה-Meta Clock מתאדים. השחקן חוזר לאפס זהב ואפס שדרוגים. בנקודה הזו, מערכת היחסים ביניכם לבין השחקן נשברת. הם לא מתחילים ריצה חדשה; הם מתחילים טינה חדשה.
הפרדת השעונים היא לא רק בחירה סגנונית. זו אסטרטגיית הישרדות.
איך Phaser v4 מטפל בפיצול
בניתי את Neon Survivor ב-Phaser v4, שמציע שני כלים ספציפיים לבעיה הזו.
ה-Registry מחזיק נתונים חיים בזיכרון עבור הסשן הנוכחי. הוא מהיר. הוא פשוט. הוא גם מתאדה ברגע שהשחקן מרענן את הדף.
LocalStorage שומר נתונים בדפדפן עצמו. הוא שורד סגירת טאבים, הפעלה מחדש של הדפדפן וניתוקי חשמל. הוא גם איטי ופחות אמין. דפדפנים יכולים לחסום אותו, להגביל אותו או למחוק אותו אם מכסות האחסון מתמלאות.
בחירת העיצוב שלי הייתה קשיחה. ה-Registry הוא מקור האמת היחיד במהלך המשחק. המשחק קורא ממנו, כותב אליו וסומך עליו לחלוטין. LocalStorage לא פועל ככותב משני. הוא פועל כמראה.
כך עובדת הזרימה: המשחק כותב רכישת שדרוג ל-Registry. מחלקת מנהל (manager class) אחת עוקבת אחרי ה-Registry. כשצריך, המנהל הזה משקף את נתוני ה-Registry ל-LocalStorage. אם הדפדפן חוסם את הכתיבה, המשחק לא נתקע. אם האחסון נכשל, הסשן הנוכחי עדיין רץ בצורה מושלמת. השחקן עלול לאבד התקדמות רק אם הוא סוגר את הטאב בדיוק באותה שנייה, אבל הסשן עצמו לעולם לא קורס.
תבנית זו מונעת אסון סמוי. אם תתנו לכל מערכת לכתוב ישירות ל-LocalStorage, תיצרו תלות ב-API שביר. שחקן עם הגדרות פרטיות מחמירות או מכשיר עם זיכרון אחסון נמוך עלול לחוות האטה או קפיאה של המשחק במהלך קרב כי פונקציית רקע ניסתה לשמור סטטיסטיקות. על ידי הפיכת ה-Registry למקור האמת היחיד, אתם שומרים על האקשן מהיר ועל הסיכון מוגבל.
תנו לקוד לנשום
השתמשתי גם באירועים (events) כדי להפריד בין המערכות. כשריצה מסתיימת, ה-GameScene לא מטפל בלוויה של עצמו. הוא לא קורא לפונקציית שמירה. הוא לא מייבא כלי אחסון. הוא פשוט משדר אירוע "run-ended" עם payload של הנתונים הרלוונטיים.
A separate listener handles the bookkeeping. It receives the event, updates the Meta Clock, and tells the manager to mirror the new totals to LocalStorage.
This separation pays off immediately. I can rewrite the entire GameScene, swap out the player character, change the camera angle, or even shift the genre from survival to bullet hell without touching the save system. The systems are independent. They talk through events, not direct function calls. That means fewer merge conflicts, fewer bugs, and a codebase that does not turn into spaghetti after six months.
Upgrades That Change the Game
Having a technical backbone is useless if the rewards feel like a spreadsheet. I spent a lot of time on how upgrades actually feel to play.
Some upgrades are safe. Extra movement speed. Bonus health. Faster reload. These give the player more room for error. They are comforting. They shrink the game without changing its rules.
Other upgrades rewrite the rules entirely. In Neon Survivor, I added "Piercing Rounds." Before this upgrade, a bullet stopped on the first enemy it hit. After the upgrade, it punches through enemies, potentially clearing entire lines in a single shot.
The difference is dramatic. Speed and health might let you survive longer, but Piercing Rounds changes how you position yourself. You start lining up enemies. You stop kiting around the edges and start cutting through the center. The decision space of the game expands.
Good progression should change a player's decisions, not just increase their numbers. If every upgrade is a percentage bump, the player stops reading the descriptions. They click, they upgrade, they forget. If an upgrade makes them rethink their strategy, they remember it. They talk about it. They come back to see what else might flip the game on its head.
The Real Payoff
The "one more run" loop is not a single system. It is a relationship between loss and gain, built on clean architecture and meaningful rewards.
Build two distinct timelines and protect the Meta Clock like it holds your players' trust, because it does. Use your engine's tools to keep live data fast and persistent data safe. Decouple your scenes from your storage so you can iterate without fear. And when you design upgrades, ask whether they give the player more time, or more interesting choices.
Get this right, and your players will not just tolerate death. They will depend on it. Every run becomes a down payment on the next one. The game stops being a series of restarts and becomes a single, continuous climb.
