How Pizza Tycoon simulated traffic on a 25 MHz CPU
核心洞见
1994年的DOS游戏《Pizza Tycoon》如何在25 MHz 386 CPU上实现流畅的城市交通模拟?答案是:拒绝复杂算法,用简单到近乎粗糙的方法。
关键技术创新
1. 没有路径规划 - 道路本身就是方向
每个道路瓦片本身就编码了车辆行驶方向:
- road tile 0x16 = 从左向右
- road tile 0x06 = 从右向左
- 0x26 和 0x36 = 垂直交通
车辆不需要知道去哪里,只要知道自己在哪块瓦片上,就能一直开。 这消除了A*、Dijkstra等路径算法的需求。
2. 拐角处的随机决策
拐角瓦片允许两个方向(如向西或向南)。当车辆到达拐角时:
- 50% 概率直行
- 50% 概率转弯
额外规则:刚转过左转后,下个拐角强制直行(避免连续左转)
3. 极其高效的碰撞检测 O(n²)
作者尝试过复杂的网格锁定系统,最后发现原始游戏的简单方法最好:
// 对每辆车,检查它是否与列表中其他车重叠
for each car:
for each other_car:
// 第一步检查方向 - 如果方向永远不可能相撞,立即返回!
if (car.direction != other_car.direction) continue;
// 同一方向的车只需检查是否在同一车道
// 大部分检查在几行CPU指令内就返回了
// 实际需要坐标计算的只有个位数情况
对于25辆车的典型城市视图,有625次成对检查,但:
- 约一半在方向检查时直接返回(东西向永不对撞)
- 大部分在车道检查时失败(同一方向的车必须在同一条路上)
- 真正需要坐标运算的只有个位数
4. 双速移动策略
- 每帧移动1像素 - 流畅动画
- 每16像素运行一次瓦片逻辑 - 检查方向变化、sprite切换
车辆生成时,progress计数器设为1-16之间的随机值,错开所有车辆的瓦片边界检查,平摊计算负载。
5. 边界重生机制
当车辆驶出屏幕边缘:
- 立即在对面方向重生
- 随机新颜色
这意味着游戏永远不需要真正"删除"车辆,只需要重新定向。
工程启示
"我带着现代概念进入这个问题:场景图、路径规划、碰撞检测,当然还有足够的CPU来运行这一切!"
作者花了14年才理解原始代码为何如此简单——因为约束本身就是解决方案。1994年的程序员没有选择,只能找出最简洁的算法。
vs 现代实现
| 现代尝试 | 原始Pizza Tycoon |
|---|---|
| 共享锁定的网格系统 | 每辆车独立判断 |
| A* 路径规划 | 瓦片自带方向 |
| 复杂碰撞层次 | 方向预过滤 + 快速 bail-out |
| 对象池管理 | 屏幕边缘重生 |
结论
在资源受限的环境中,最简单的方案往往是最有效的。原始程序员不是不懂复杂算法,而是在25 MHz CPU的约束下,被迫找到了最优解。
这个案例呼应了"做减法"的哲学:有时候,最优雅的代码是那些拒绝添加本不需要的东西的代码。