嘿,朋友们:
我过去经常玩生存游戏,也尝试过类似《环世界》的模拟类游戏。这些游戏总会在我脑海中衍生出有趣的故事。我第一次玩《环世界》时,在游戏里捏了自己、妻子和一位朋友——结果我的妻子变成了酗酒抑郁的人,突然罢工了(精神崩溃)。她死后,我在屋后挖了个坑把她埋了。更惨的是,我最后一位朋友被海盗绑架了,这太让人难过了😢,但酷的是我知道他还活着,理论上我还能救他。这段经历实在太棒了,我觉得未来很长一段时间都不会忘记。
我本是嵌入式开发出身,从未做过游戏。但我经常思考这类游戏是怎么开发的,然后想到:以我的背景和经验,我会如何制作这样一款游戏?
我在一个高度管控、规范化的环境里工作,深知可追溯性的重要性。这让我思考NPC(非玩家角色)之间共享的记忆(甚至可能跨越世代)。在《环世界》里虽然有日志能查看NPC的遭遇和互动,但我觉得这些机制不够深入,也几乎不像现实生活那样产生后果。所以我开始琢磨NPC的记忆系统——他们会互相交谈,用记忆构建对话内容和话题。我还想到:如果模拟持续1000年(作为科幻迷,我第一反应是世代飞船),那么几百年前某人说过的话或做过的事,还会产生影响吗?(《云图》就是关于这个主题的好书。)于是我想试试看。
关于"记忆一致性持续1000年"这个问题,我的第一个答案是绝对确定性。我需要一个模拟系统:无论启动多少次,无论运行在什么操作系统或机器上,种子(seed)、tick次数(tickcount)、哈希值(hash)都完全一致。浮点数计算就是个问题,所以我用了定点数,否则无法在不同机器/系统间保持确定性。我还构建了"黄金运行"作为变更检测器(1比特变化就会导致哈希完全不同)。这意味着如果哈希值在没有规则变更的情况下改变,说明模拟有bug。确定性还在,但复现性没了。如果在相同种子、没有规则变更的情况下,哈希值跨多个构建版本都不变,那说明一切正常。这样我就拥有了一套极佳的测试机制。但当我改变规则时,哈希值也必须改变,我需要重建新的基线。
但这还没完,还有两个问题:字典迭代顺序取决于键的哈希值和内部表布局。顺序对复现很重要,因为谁先得到有限的水或食物、谁得不到,会影响整个模拟结果,进而影响哈希值。而且在.NET中,进程启动时会随机计算一个值并混入哈希计算(这是安全特性),但对我的模拟来说,同样的字符串在两次程序运行中会产生不同哈希值,导致迭代顺序不同。所以每次重新启动程序都会产生不同顺序和不同哈希。我的解决方案是:永远不要按插入顺序迭代,而是始终按ID排序。
另一个问题是内容文件:不同操作系统的不同文件系统会按各自偏好顺序返回目录。所以我必须把加载顺序写进清单(manifest),并解决ID冲突。
第一次模拟只有几个人:他们有年龄,需要水和食物,有健康状态——测试运行顺利通过了。也就是说他们成功存活了一千年😊。我现在已经推进了一点,感觉走在正确的道路上。我真的沉浸在这个课题里了,工作之余非常享受。
我想知道是否有人也在游戏开发中遇到过模拟系统以及这种绝对确定性的问题?你们遇到了哪些问题?
评论 (0)