当年的游戏公司没有成熟引擎,他们都是怎么"手搓"游戏的?


这个问题的答案比大多数人想象的要野蛮得多。

现在的游戏开发者打开Unity或者Unreal,编辑器在那里,物理引擎在那里,渲染管线在那里,光照系统在那里,你要做的是在一个已经搭好的框架里填内容。这件事放在1980年代是不存在的,那个年代没有框架,没有引擎,没有可以复用的基础设施,你想做一个游戏,你先得造出来一台能跑游戏的机器,然后在这台机器上用汇编语言告诉它每一个像素应该是什么颜色。

从像素开始。

早期游戏的画面是直接写进内存的。程序员需要知道屏幕上每一个位置对应的内存地址,想在某个位置画一个点,就往那个地址写一个值,值代表颜色。想画一条线,就写一个循环,告诉CPU从这个地址到那个地址,每隔多少字节写一次。想画一个角色,就把角色的每一行像素转成十六进制数,一行行写进去,然后在屏幕上找到正确的偏移量,把这些数放进去。

这套操作今天叫做“软件渲染”,当时没有名字,因为没有别的选择,它就叫做“画面怎么出来的”。

卡马克在做第一款德军总部的时候面对的是一个具体问题:IBM PC的显卡架构不支持平滑横向滚动,屏幕更新的方式是整块刷新,导致画面移动的时候会撕裂,看起来像在抖动。他花了大量时间研究显卡的底层寄存器,找到了一种可以骗过硬件的写入方式,用软件模拟出硬件不支持的功能,然后画面就能平滑滚动了。

这件事没有文档,没有前人经验,是他自己坐在那里把硬件手册翻烂,然后一点一点试出来的。

这是那个年代程序员的基本工作状态,你面对的不是一个有说明书的系统,你面对的是硬件本身,你得比硬件更了解硬件,然后在硬件的缝隙里找到空间。

声音是另一个维度的问题。

早期PC的声音系统是一个蜂鸣器,PC Speaker,它只能发出一种声音,就是嘀的一声。想用它播放音乐,你要做的是控制这个蜂鸣器的振动频率,用极快的速度切换不同频率,利用人耳的残留效应,让它听起来像是在播放旋律。这个技术叫PWM,脉宽调制,原理是声音物理学,实现是精确到微秒级别的时序控制,写错一点,出来的就是噪音。

当时有程序员专门研究怎么从PC Speaker里榨出更多层次的声音,方法是在极短的时间窗口里切换不同频率,制造出伪立体声的效果。这套技术在当时的demo场景里被当成黑魔法,因为你用一个只会嘀一声的蜂鸣器,弄出了接近音乐的东西,别的程序员听了不知道你是怎么做到的,因为原理不直觉,需要你对时序控制有极其精确的理解才能实现。

Adlib和Sound Blaster出来之后有了FM合成音效,这是另一套需要从头学的系统,FM合成的原理是用一个频率的波形去调制另一个频率的波形,产生复杂的音色,不同的调制参数产生不同的乐器音色,程序员要自己调参数,自己测试哪个参数组合听起来像钢琴,哪个像小提琴,哪个像爆炸声。这件事没有标准答案,完全靠耳朵和经验,当时每家公司的声音程序员都有自己积累的参数表,那是真正意义上的商业机密。

游戏逻辑层面的设计感则是另一种形态。

没有引擎意味着没有现成的碰撞检测,没有现成的物理模拟,没有现成的AI框架。你想让一个角色在走到墙边的时候停下来而不是穿墙而过,你需要自己写碰撞检测,自己定义什么叫做“碰撞”,自己决定碰撞发生之后角色的速度和位置怎么变化。

早期游戏的碰撞检测方案简单粗暴,把角色和障碍物都看成矩形,判断两个矩形是否重叠,重叠了就停下来。这套方案快,但问题是真实的游戏角色不是矩形,是有形状的精灵图,矩形碰撞盒和实际图形之间永远有误差,所以早期游戏里经常出现子弹明显打偏了但还是判定命中,或者角色明显还没碰到墙但已经停住了的情况。

玩家当时不叫这是bug,叫这是游戏特性,因为他们不知道还可以不这样。

另外一件值得一提的是,早期PC的内存极其有限,640KB是长达十年的硬件上限,这个数字今天连一张截图都存不下。程序员要把游戏的所有内容——图形、音效、地图、逻辑代码——全部塞进这640KB,还要给运行时的数据留出空间。

怎么塞?压缩,分块加载,复用。

图形数据压缩是一门独立的技术,程序员要自己发明压缩算法,让图形数据在存储时尽量小,在需要显示的时候即时解压。地图太大放不下就分成块,玩家走到哪块就加载哪块,走出去就卸载,这套技术今天叫流式加载,当时叫没有办法。

内存里的每一个字节都是争来的,程序员在写代码的时候要随时想着,这个变量我能不能用更小的数据类型,这段逻辑我能不能少用几个临时变量,这个循环我能不能减少几次内存访问。这种思维方式今天的程序员已经基本不需要了,现代硬件的内存足够宽裕,编译器的优化足够智能,你不需要这样精打细算。

但那个年代的程序员脑子里装着整台计算机的运作方式,从CPU指令周期到内存布局到显卡寄存器,每一层他都得懂,因为每一层都可能是他需要去钻的缝隙。

卡马克写过一段计算平方根倒数的代码,里面有一行注释是“what the fuck”,这段代码用了一个极其反直觉的位操作技巧来快速近似计算平方根倒数,比标准数学库快得多,是为了在3D渲染里节省计算时间。这段代码后来变成了程序员文化里的一个传说,被反复引用,因为它代表了一种已经消失的思维方式,在硬件限制里用数学和创意强行扣出性能的那种方式。

那个年代的游戏程序员更接近工程师和科学家的混合体,他们解决的不只是“功能怎么实现”的问题,是“在这台机器的物理极限内,功能能不能实现”的问题。

两个问题差别很大。

第一个问题有标准答案,查文档,调API,照着来。第二个问题没有标准答案,你得自己发明答案,有时候答案存在,有时候不存在,你得先搞清楚是哪种情况,然后再决定怎么办。

现在的游戏工业是一套巨大的协作体系,引擎公司、中间件公司、工具链公司,每一层都有人在做,开发者站在这个体系的顶端,专注于内容本身。这是进步,是好事,让更多人能做游戏,让游戏的规模和复杂度远超过去。

但那个从像素开始造游戏的年代,有一种今天已经不存在的东西。

你打开游戏,你知道屏幕上的每一个点是怎么来的,每一个声音是怎么发出来的,每一次碰撞是怎么判定的,因为那些都是你一行一行写出来的,没有任何部分是别人替你做的。

这种感觉有一个名字,叫做彻底的掌控感。

所以过去开发游戏的大神们,那是真的大神,真的很厉害。