最好的 Roguelike 游戏不仅仅是让你死掉,而是让你渴望去死。
这听起来很奇怪,但任何曾在午夜输掉一局,却在 12:03 又开启下一局的人,都懂这种感觉。死亡是刺痛的。你的生命值归零,屏幕被失败的景象淹没。然而,你的手指已经悬停在“开始游戏”按钮上了。过去那二十分钟里的某些体验如此重要,以至于你无法就此结束游戏。
这就是“再来一局”循环,而这并非偶然,而是精心设计的。如果你正在开发 Roguelike 或任何具有永久死亡机制的游戏,你的全部工作就是平衡两种对立的力量:玩家必须经历足够的失败来感受到紧张感,同时又必须保留足够的收获来维持希望。
失败的货币
在我的最新项目 Neon Survivor 中,我追求的就是这种紧张感。当玩家死亡时,本局收集的所有东西都会消失,唯独剩下一样东西:金币。这些金币会自动存入银行。回到菜单界面后,玩家可以用它们购买永久升级。然后他们再次投入战斗,比之前稍微强一点。
这个简单的循环支撑着整个游戏。如果没有它,死亡就是一个句号。玩家会选择离开,因为上一局什么也没带走。有了它,死亡就变成了一个逗号。这一局变成了一次资源积累之旅。损失虽然令人心痛,但也为明天支付了代价。
诀窍很简单:即使失去了其他一切,你也要保留一些东西。难点在于,要确保保留的东西既有意义,又不会破坏挑战性。如果升级太弱,玩家会失去动力;如果升级太强,游戏就会变成“自动运行”。无论哪种情况,循环都会崩溃。
两套时钟,零混乱
为了妥善实现这一点,我必须管理两套独立的计时系统。
Run Clock 在每次点击开始时都会重置。它记录生命值、当前分数、敌人波次以及在单局中获得的任何临时增益。当角色死亡时,这个时钟会回拨到零。
Meta Clock 永远不会重置。它记录了所有尝试中赚取的总金币、达到的最高波次以及购买的所有永久升级。无论浏览器刷新多少次,这个时钟都会持续跳动。
将这两者混在一起会导致难以追踪且难以修复的 Bug。我见过开发者在进行常规场景重置时,因为清理函数触碰了错误的数据存储,而不小心抹去了玩家的进度。Meta Clock 的数据瞬间蒸发。玩家回到了零金币、零升级的状态。在那一刻,你与玩家之间的关系就破裂了。他们不是在开启新的一局,而是在开启一段新的仇恨。
将两套时钟分开不仅是一种风格选择,更是一种生存策略。
Phaser v4 如何处理这种分离
我使用 Phaser v4 开发了 Neon Survivor,它为解决这个问题提供了两个特定的工具。
Registry 在内存中保存当前会话的实时数据。它速度快,使用简单,但一旦玩家刷新页面,它就会消失。
LocalStorage 将数据保存到浏览器本身。它能经受住标签页关闭、浏览器重启和断电的考验。但它也更慢,且可靠性较低。如果存储配额满了,浏览器可能会拦截、限制或清除它。
我的设计选择非常严格:Registry 是游戏过程中的唯一事实来源(source of truth)。游戏读取、写入并完全信任它。LocalStorage 不充当“共同作者”,它充当的是“镜像”。
以下是工作流程:游戏将一次升级购买写入 Registry。一个单一的管理类(manager class)负责监听 Registry。在适当的时候,该管理器会将 Registry 的数据镜像到 LocalStorage 中。如果浏览器拦截了写入操作,游戏也不会卡顿。如果存储失败,当前会话仍能完美运行。玩家只有在关闭标签页的瞬间恰好发生错误时才可能丢失进度,但会话本身永远不会崩溃。
这种模式防止了一场隐蔽的灾难。如果你让每个系统都直接写入 LocalStorage,你就会对一个脆弱的 API 产生依赖。如果玩家开启了极高的隐私设置,或者设备存储空间不足,可能会在战斗中遇到游戏变慢或冻结的情况,因为某个后台函数正试图保存统计数据。通过将 Registry 作为唯一的真相来源,你可以保持动作的流畅并控制风险。
让代码自由呼吸
我还使用了事件来解耦系统。当一局结束时,GameScene 并不负责处理自己的“葬礼”。它不会调用保存函数,也不会导入存储工具类。它只是简单地发出一个带有相关数据负载(payload)的 "run-ended" 事件。
一个独立的监听器负责处理数据记录。它接收事件,更新 Meta Clock,并通知管理器将新的总计同步到 LocalStorage。
这种分离带来的好处立竿见影。我可以重写整个 GameScene,更换玩家角色,改变摄像机角度,甚至将游戏类型从生存类转变为弹幕类,而无需触动存档系统。各个系统是独立的。它们通过事件进行通信,而不是通过直接的函数调用。这意味着更少的合并冲突、更少的 Bug,以及在六个月后也不会变成“面条代码”的代码库。
改变游戏体验的升级
如果奖励给人的感觉像是在填电子表格,那么拥有强大的技术底座也毫无意义。我在“升级带来的实际游戏手感”上花费了大量时间。
有些升级是稳健型的。额外的移动速度、额外的生命值、更快的装弹速度。这些升级为玩家提供了更多的容错空间。它们让人感到安心,在不改变游戏规则的情况下降低了游戏难度。
而另一些升级则会彻底重写规则。在 Neon Survivor 中,我加入了“穿透弹 (Piercing Rounds)”。在升级之前,子弹在击中第一个敌人后就会停止;升级之后,子弹可以穿透敌人,甚至可能一发子弹清空整行敌人。
这种差异是巨大的。速度和生命值或许能让你生存得更久,但“穿透弹”改变了你的站位方式。你会开始有意识地让敌人排成一列。你不再是在边缘进行拉扯,而是开始从中心穿插。游戏的决策空间因此得到了扩展。
良好的成长体系应该改变玩家的决策,而不仅仅是增加数值。如果每一次升级都只是百分比的提升,玩家就会停止阅读描述。他们点击、升级、然后忘掉。如果一次升级能让他们重新思考策略,他们就会记住它,讨论它,并再次回来看看还有什么能彻底颠覆游戏体验的东西。
真正的回报
“再来一局”的循环并非单一的系统。它是损失与收益之间的关系,建立在整洁的架构和有意义的奖励之上。
构建两条截然不同的时间线,并像守护玩家的信任一样守护 Meta Clock,因为事实确实如此。利用引擎工具确保实时数据的快速响应和持久化数据的安全。将场景与存储解耦,以便你可以毫无畏惧地进行迭代。在设计升级时,问问自己:它们是给了玩家更多的时间,还是给了玩家更多有趣的抉择?
做好了这一点,你的玩家将不仅仅是忍受死亡,他们会依赖死亡。每一次尝试都成了下一次尝试的铺垫。游戏不再是一连串的重启,而变成了一场单一且持续的攀升。
