Pizza Tycoon 如何在 25 MHz CPU 上模拟交通系统

查看原文 HN 讨论

文章摘要

这篇文章来自 Pizza Legacy 项目博客,作者 cowomaly 详细介绍了他如何通过逆向工程揭示 1994 年 DOS 游戏《Pizza Tycoon》在仅 25 MHz 的 386 CPU 上实现城市交通模拟的秘密。Pizza Legacy 是该经典游戏的开源重新实现项目,作者从 2010 年开始开发,花了 14 年才最终让汽车在街道上以令人满意的方式行驶。

作者多年来多次尝试实现交通系统,但每次都陷入过度复杂的设计中。2017 年的一次尝试中,他让每个地图格子追踪被占用的位置,每辆车移动前都需要向网格申请许可、预留和释放位置——基本上变成了一个共享锁定系统。与此同时,他一直有一个困惑:原版游戏在 25 MHz CPU 上就能运行这套系统,为什么他的版本总是这么复杂?

最终他深入研究了原版的汇编代码(借助 LLM 工具辅助理解),发现了关键洞见:汽车不需要知道自己要去哪里。每种道路地图块自身携带行驶方向信息——例如地图块 0x16 是水平道路的下半部分,汽车只能从左向右行驶。城市本质上就是一堆单行道。遇到拐弯地图块时,汽车以 50% 的概率决定是直走还是转弯,并有一条额外规则防止连续左转以保持交通看起来自然。

移动系统同样简洁:汽车每帧移动一个像素。一个从 16 递减到 1 的进度计数器控制地图块边界逻辑的执行频率——因为每个地图块为 16x16 像素,所以这段较重的逻辑每 16 帧才运行一次。汽车生成时进度计数器设为随机值,使所有汽车的边界检查错开分布在不同帧上。

碰撞检测使用简单的 O(n^2) 两两比较,但通过极其高效的提前退出策略,绝大多数配对几乎不消耗计算资源:首先检查方向——东向和西向的车永远不会相撞(因为是单行道),这一步就排除了约一半的配对;然后检查是否在同一条道路上(一次等值比较);真正需要坐标计算的配对通常只有个位数。被阻塞的车会等待 10 帧,自然形成交通拥堵和疏散的效果。

生成策略也很聪明:进入近景时扫描视口内所有道路格子,根据区域交通密度决定是否生成汽车;驶出屏幕边缘的汽车会被回收为反方向的新车重新生成。作者总结说,他失败的尝试都是在为原版根本不需要考虑的问题做设计——不需要寻路因为地图已经告诉汽车该去哪、不需要物理引擎因为每帧一像素就够逼真了。

HN 评论精华

  1. setr 指出通过将道路设为单向并让道路本身控制移动方向,这个系统本质上等价于传送带——这意味着 Factorio(异星工厂)的传送带优化策略可能也适用于此。例如存储物品之间的间距差值而非追踪绝对位置,因为同一条道路上车与车之间的距离在大部分时间是静态的。他还附上了 Factorio 开发博客中关于传送带优化的链接。

  2. Waterluvian 对方向箭头图示产生了一个小小的顿悟时刻:你根本不需要复杂的交叉路口规则,你不必推理”交叉路口”本身,而是推理”车道”!在每个选择点,只需将转弯的权重设低于直行即可。另一条评论中他还提问是否有”在如此低的硬件要求上不应该能运行”为主题的社区或 Game Jam,类似 Demo Scene 但面向游戏。

  3. edwcross 分享了自己玩《Pizza Tycoon》的回忆:他曾经在柏林(最便宜的城市)买下所有商业地点以避免竞争,主要靠”冰淇淋”(非法武器)交易赚钱——在不同城市之间低买高卖。但买了大约 200 个商业地点后游戏总是会崩溃。他还提到最有趣的是尝试各种奇怪的披萨配方,发现口味算法有些奇特。

  4. Neywiny 提出了一个重要区分:不是说不需要寻路是因为道路告诉车该去哪,而是因为车本来就不需要去任何特定地方。与《城市天际线》等游戏中车辆从 A 点到 B 点不同,随机方向选择在交叉路口是一个完全不同且简单得多的问题。

  5. manofmanysmiles 感叹这个项目已经开发了这么长时间,认为这在当下狂热的”氛围编程”(vibe coding)时代是一股清新的空气,也提醒自己可以放慢脚步。