☰
游戏引擎演进史:从一套代码一个游戏到一套框架支撑一堆游戏
2026/10/2 19:36:48 网站建设 项目流程

真要说起来,“游戏引擎”这个概念,不是被某个人坐在办公室里灵光一闪“发明”出来的,而是被一堆游戏项目前赴后继“逼”出来的。今天你打开Unity新建一个场景,拖一个Cube进去,写三行脚本让它转起来,整个过程不超过五分钟;但放在三十年前,这件事的代价是:你得为某一块特定硬件重新写一套显示、输入、声音和游戏逻辑,而且这套代码大概率只属于这一个游戏,下个项目从零再来。从“一个游戏一套代码”到“一套框架支撑一堆游戏”的转变,就是游戏引擎诞生和演进的全部故事,也是这个系列文章在第一篇准备讲透的东西。

这篇文章我会按“从无到有、从大到强、从封闭到开放”的脉络来聊,主要面向两类读者:一类是想系统学习游戏引擎原理、准备入行或者正在写自己小引擎的新人;另一类是已经在用某个商业引擎做项目、经常被各种机制性bug折磨的从业者。说句实在话,你以后在工作里遇到的很多“诡异问题”,根子都藏在引擎的历史设计决策里。把这一课补上,比多刷二十个教程视频管用得多。

1. 引擎概念诞生之前:一套代码只能做一个游戏的年代

1.1 雅达利与红白机时代:连操作系统都要游戏自己当

上世纪七八十年代,游戏机不是我们现在理解的“运行游戏的平台”,而是“为某一个游戏专门焊死的电路盒子”。雅达利2600上的游戏,ROM容量只有几KB到几十KB,程序员直接对着6502汇编写代码,连画面都不是在显存里画好再输出,而是跟着电视的电子束扫描线“边画边输出”——你必须在极短的时间内把当前这条扫描线的像素计算出来。所以当时的游戏程序为了节省指令周期,经常用各种野路子:把角色精灵的数据藏在代码里、把分数累加放在中断里,代码和美术资源是彻底纠缠在一起的。

在红白机时代,情况稍有改善,但本质没变。任天堂的卡带里通常是一个完整的ROM镜像,启动后CPU直接执行,没有操作系统、没有文件系统、没有任何可供其他游戏复用的运行库。所谓“游戏”,从画面渲染、碰撞检测,到手柄输入、音频播放,再到关卡数据和业务逻辑,全部杂糅在一个二进制里。

那时候的人不会说自己“用了一个引擎”,因为根本没有引擎这个概念。他们只是写了一个游戏,顺便把那个游戏需要的所有底层能力都亲手实现了一遍。你可以想象成拍一部电影,你得自己造摄影机、造录音机、造剪辑台,而且拍完这部电影,这些设备全部报废,下一部电影从头再造一套设备。

1.2 为什么那个年代根本长不出“引擎”这种东西

现在回头看,早期没有引擎,不完全是因为技术水平低,而是有三个硬性条件决定了“引擎”不可能出现。

第一,硬件极度碎片化。街机、家用机、掌机,每种机器用的CPU、显卡芯片、声音芯片都不一样,连“往屏幕上画一个点”这个动作在不同设备上都要用完全不同的方式实现。在这种环境里积累“可复用代码”的成本高得吓人,而且一旦换了平台,之前的积累基本归零。

第二,没有标准化的图形抽象层。今天的DirectX和OpenGL把显卡变成了一个“标准接口”,而当时的开发者面对的是赤裸裸的硬件寄存器。每个游戏的渲染代码都是针对特定显卡定制的,换个显卡游戏就跑不起来,更不用说你拿着这份代码去跑另一个游戏。

第三,商业模式上没有复用的动力。游戏是按“完整产品”卖出去的,卡带烧录之后内容就封死,没有补丁、没有DLC、更没有Mod。既然不打算往一个游戏里持续加内容,那“把数据与逻辑分离”这件事就失去了动机。一个游戏的代码能把自己跑通,就是胜利。

所以那个时代的程序员,与其说是在“开发游戏”,不如说是在“驯服硬件”。等到后来PC开始流行,屏幕分辨率越来越多样化、硬盘越来越大、网络联机开始出现,大家才终于意识到:如果每个项目都把底层重写一遍,团队根本活不下去。

2. 转折点:Doom到Quake,游戏引擎是怎么被“逼”出来的

2.1 Doom的WAD:第一次把“程序内核”和“游戏内容”切开

业内公认的“引擎”概念真正成型,通常要追溯到1993年的id Software和它出品的《毁灭战士》(Doom)。Doom最革命性的地方不是画面多震撼——而是它的数据组织方式。游戏运行时由一套“核心程序”构成,而地图、贴图、声音、怪物种类等所有可替换内容,全部打包在一种叫WAD的文件里,WAD全称是“Where's All the Data?”,翻译过来就是“数据都在这呢”。这个命名方式本身就透着一股“咱们把数据和代码分清点”的意味。

这么做的最直接动机,是id内部的工作流被逼得没办法了。地图设计师在关卡编辑器里改来改去,如果每改一次地图都要重新编译C代码,那游戏根本做不完。把地图从程序里剥出去,设计师就能在不碰代码的情况下改关卡,程序员也能专心优化渲染和玩法逻辑。这正是“数据驱动设计”最早的实践。

WAD还带来一个谁也没想到的副产品:玩家可以自己做地图。只要你会用编辑器,就能往WAD里塞你设计的关卡,于是第一个真正意义上的游戏Mod社区诞生了。今天你们说的“引擎是给内容创作者用的工具”,在1993年就已经被Doom用实践证明了。

我一直觉得,理解“引擎=内核+内容”这个二元结构,是看懂所有现代引擎的一把钥匙。你今天在Unity里做的Prefab、在Unreal里做的Level、在Godot里做的场景,本质都是WAD的现代变体:一套运行时引擎加上一堆可被加载、可被替换的内容数据。

2.2 Quake的三大遗产:真3D、客户端/服务器模型、开源教科书

如果说Doom确立了引擎的数据驱动思想,那么1996年的Quake就是把引擎推向“3D化”和“网络化”的分水岭。Quake是第一个真正意义上的全3D引擎,但它留下的遗产远不止多边形渲染。

头一个遗产是“客户端/服务器”架构。Quake当年的单人模式,本质上也是一个“本地网络游戏”:玩家本地跑着一套模拟世界的代码,而底层通讯走的却是网络协议栈,哪怕对方就是本机自己。这个设计在当时看似绕路,却让联机变得水到渠成。你现在的每款联网游戏,不管是FPS还是MOBA,底层逻辑几乎都沿用这个模型:服务端是唯一权威,客户端传输入、收状态。

第二个遗产,是“引擎授权”这门生意的起点。Quake火了之后,无数工作室想直接拿这套渲染和网络代码做自己的游戏。id顺势把引擎打包授权出去,《半条命》最初就是在Quake引擎基础上深度改造而来的。从这时候起,“引擎”从一套内部工具变成了一个可销售的产品,商业引擎市场正式开始运转。

第三个遗产可能最被低估:开源。Carmack后来陆续公开了Doom、Quake等引擎的源码,这些代码就是那个年代所有引擎开发者的免费教科书。无数后来的游戏程序员,是靠一行行读Quake的源码学会BSP、光照贴图和网络同步的。这种“开源教科书”模式,也让“写引擎”从少数大厂的黑科技变成了一门可以学的技术,这为后来Unreal和Unity的崛起储备了整整一代人才。

2.3 另一条路线:Build引擎和2.5D的顽固生命力

这里想提一嘴可能已经被很多人遗忘的Build引擎——就是《毁灭公爵3D》用的那个东西。在Quake赌上全3D的同时,Ken Silverman用Build引擎走出了另一条路:用“区块+墙面”的方式描述一个可探索的3D空间,但视角和计算本质上是2.5D的。这玩意儿在《毁灭公爵3D》里表现出的空间感和易玩性,放到今天看依然很能打。

Build引擎告诉我们一个道理:引擎的历史不是一条笔直的进化线,而是多条技术路线在同一个时代并行竞争。判断一个引擎好不好,从来不是看它的技术指标最先进,而是看它有没有匹配当下游戏制作的核心需求。Build引擎满足了当时最火的“房间探索+射击”玩法,这就够了。这一点到今天依然成立:你选引擎,选的是匹配度,不是跑分。

3. 现代引擎骨架成形:编辑器与运行时如何“双核”驱动

3.1 Unreal的迭代史:编辑器才是第一生产力

1998年Epic带着《虚幻竞技场》和Unreal引擎出现在市场上时,最大的杀手锏还不是画面,而是配套的UnrealEd编辑器。这是很多开发者第一次在一个引擎里做到“所见即所得”:美术在地图编辑器里放置模型、设定灯光、布置触发器,不需要重新编译程序就能直接进游戏里验证效果。同时Unreal还配套了UnrealScript脚本语言,让策划可以在不碰C++的情况下改玩法逻辑。这套“编辑器+脚本+运行时”的组合拳,到今天依然是商业引擎的标配。

此后Unreal的每一次迭代,本质上都在做两件事:把编辑器做得更强,把渲染做得更真。UE2巩固了工具链,UE3在主机平台上大杀四方,催生了《战争机器》这一代产品;UE4引入了基于物理的渲染(PBR)和蓝图可视化脚本,把“写玩法”的门槛进一步降低;到了UE5,Lumen实时全局光照和Nanite虚拟化几何体,直接把“影视级画面”的实时化变成了可能。

我身边不少朋友做项目选引擎都纠结画质,其实Unreal真正厉害的地方不在渲染算法本身,而在于它把“大规模团队协作生产内容”这件事的流程理顺了。引擎竞争到这个阶段,比的已经不只是运行时快不快,而是内容团队用得顺不顺。毕竟游戏是几百号人一起做出来的,工具链的顺畅程度直接决定项目能不能按期交付。

3.2 Source引擎:物理、模组生态和“软实力”竞赛

2004年《半条命2》发售,Source引擎刷新了很多人对“引擎软实力”的认知。它在渲染之外整合了Havok物理引擎,于是游戏里的桶可以被踢翻、木板可以搭桥、橡皮艇能随水流起伏——物理不再是几个简单的碰撞盒模拟,而是成了一个能支撑大量玩法设计的系统。Source还折腾了面部表情动画和材质模拟,这些在当时看着是炫技,后来都成了FPS游戏的常用语言。

更关键的是Source SDK和Steam创意工坊带来的模组生态。当初《反恐精英》就是从Quake/半条命Mod一路长成的,Garry's Mod更是完全建立在Source引擎的实体和物理系统之上。一个引擎能够允许玩家持续改造内容,它就不再只是一个开发工具,而是一个社区平台。今天你在Unity里看到大量对话式叙事游戏、在Godot里看到一堆Jam作品,本质上都是这种“引擎作为平台”思路的延续。

所以评价一个引擎,“有多少官方功能”只是一半,“社区能在它上面长出什么”是另一半。这也是为什么我总觉得,刚学引擎的时候别只看官方文档,去翻翻那些基于同一引擎的Mod作品和独立游戏,收获往往更大。

3.3 为什么游戏循环和场景树是通用零件

讲到这里,可以把现代引擎的通用骨架抽出来了。不管Unreal、Unity还是Godot,它们的运行时都逃不开这几个零件:

  • 游戏循环:不停重复“处理输入→更新逻辑→渲染画面”的过程,每转一圈就是一帧。
  • 场景图/场景树:用树状结构组织场景里的所有物体,父节点的位移会影响子节点。
  • 组件系统:把位置、渲染、物理、脚本等能力拆成可组合的小零件,挂在物体上。
  • 资源管理:加载、缓存、卸载贴图、模型、音频等数据,让内容可被流式读取。

为什么全世界的引擎最后都收敛到这套结构?因为每个游戏本质上都要回答三个问题:现在的“时刻”推进到了哪里、场景里有什么东西、这些东西各自怎么表现。游戏循环回答了时刻,场景树回答了有什么,组件和资源系统回答了怎么表现。这三个问题躲不开,所以这套骨架就成了行业共识。

理解了这套骨架,你调试时的心态都会不一样。比如物体穿墙、跳帧卡顿这类问题,往“游戏循环的更新频率”和“物理步长”上一靠,基本一眼就能定位,而不是对着代码瞎猜。

4. 引擎平民化:Unity、CryEngine和中间件的合纵连横

4.1 Unity靠组件化与全平台打开了闸门

Unity是2005年出现在市场上的,它的革命性在于把“引擎”从大厂专供变成了普通开发者的日用品。Unity最核心的设计哲学是组件化:一个游戏物体(GameObject)只是一个空壳,所有行为——可见吗、会动吗、有碰撞吗、响不响——都通过往上挂不同组件来实现。你不需要继承某个庞大的“Actor基类”,而是像拼积木一样把Rigidbody、Collider、AudioSource、MonoBehaviour脚本拼到一起。

这套设计的最大好处,是在运行时极度灵活:同一个物体可以在运行中动态增删组件,项目里也能方便地做组合式玩法。配合C#这门现代语言,开发者写逻辑的效率比写C++和专属脚本快不少,再加上从PC到移动端“一次编写多平台打包”的能力,Unity几乎精准踩中了移动游戏爆发的那波浪潮。

我见过很多从Unity入行的新人,完全不知道这套“组件组合”的思路在当时有多超前。你要是经历过上一代引擎的深继承体系就知道,深度继承很容易陷入“一个子弹类从Actor类那继承了一条几十层的逻辑链”这种僵化结构,改一处影响一大片。组件化是把“继承”换成“组合”,游戏逻辑的组织方式从家族谱系变成了工作台装配,灵活度完全不同。

4.2 CryEngine的教训:技术标杆不等于商业成功

和Unity几乎同期登场的CryEngine,走的是另一条极端路线:技术画质拉满。2004年的《孤岛惊魂》和2007年的《孤岛危机》至今还是“显卡危机”的梗来源,CryEngine在植被渲染、光照、水面效果上一度大幅领先同行。然而它在商业授权上步履蹒跚,工具链和文档对外部团队不够友好,结果在第三方游戏阵容上始终没打开局面,后期即使宣布免费也无力回天。

CryEngine的例子特别适合用来纠正一个思维误区:以为技术最牛就有人用。其实一个引擎要普及,需要的是一整套东西——稳定的版本、可读的文档、活跃的社区、合理的授权条款、顺畅的内容生产管线。画质只是无数个因素里的一项,而且远不是最重要的一项。今天你用Unreal,也不是因为它比CryEngine画面好多少,而是因为它整个生产体系更成熟。

4.3 中间件军团:引擎背后看不见的生态位

如果只盯着Unity、Unreal、CryEngine这几个名字,你会误以为引擎是一个封闭的单体。真实情况是,商业引擎早就和一堆中间件深度绑定:物理有Havok和PhysX,音频有FMOD和Wwise,植被有SpeedTree,动画和UI也各有专业工具。引擎更像一个总管,负责把各种专业软件伺候的资产统一调度到运行时里。

这种“引擎+中间件”的局面,解释了为什么现代引擎的架构里到处是“适配层”——想办法把不同第三方工具的资产导入、序列化、运行时表现统一起来。你做项目时也可以反过来利用这一点:与其什么都让引擎原生搞定,不如研究一下给它接上合适的中间件,音频、本地化、物理表现往往能提升好几个档次。

5. 开源世界的再进化:Godot、Mod注入和那些绕不开的老坑

5.1 Godot凭什么在这几年站起来

Godot是2014年前后开始进入公众视野的开源引擎,它的许可证是MIT——意味着你拿它做任何商业游戏都免费、无分成、无登录要求。这类完全自由的授权方式,让它在独立开发者、教育机构和小型团队里积累了极高的好感度。

从技术上来说,Godot最出名的是它的场景系统:一个场景可以是一个按钮、一个敌人、甚至是一整张地图,所有东西都由“节点”拼成,而场景之间可以互相实例化和继承。这套设计对2D的支持尤其扎实,编辑器轻量、启动快,GDScript脚本语言读起来也像Python一样直观,上手门槛比主流商业引擎低一大截。

当然,Godot也有它的软肋:重型3D的生态成熟度、海量商业素材的接入便利性、大规模项目团队协作的工具链,都还在追赶Unity和Unreal。但如果你做的是2D游戏、教学项目、小程序,或者你只是不想被商业引擎的授权条款绑住手脚,Godot绝对值得放进备选清单。我个人的判断是,开源引擎接下来的成长速度会出乎很多人意料——历史规律摆在那里,当成本降到零且社区密度足够高时,生态总会自己长出来。

5.2 BepInEx为什么能“注入”那么多游戏引擎

这几年有个热门词,总有人问“BepInEx可以注入哪些游戏引擎”。这里稍微展开说下。BepInEx本质上是一个插件注入框架,最常见的应用场景是Unity游戏:它利用很多Unity游戏自带的Mono运行时,在游戏进程启动时抢先加载一个.NET程序集,让你能用C#写Mod去挂钩游戏内部方法、替换资产、修改逻辑。像《英灵神殿》这类Unity游戏,社区Mod基本都靠它支撑。

为什么它能“注入”那么多游戏?因为不少引擎(尤其是Unity和一些基于.NET生态的游戏框架)在发布时保留了托管运行时,这就像门没有锁死——对Mod作者来说是福音,对想逆向分析游戏的人也是明路。作为开发者,你必须意识到这一点:你的游戏包在别人手里是可以被拆开看的,Mono的C#程序集甚至可以被反编译成几乎接近源码的程度。

这里不想讨论任何灰色话题,但Mod生态和引擎开放度的关系值得每个开发者思考。一个引擎是否支持Mod,直接决定它的社区能不能自发产出内容生态。这也是为什么Valve、Bethesda这些老牌公司一直砸资源做Mod工具——它们清楚,玩家产出的内容会让一个游戏活很多年。你今天做项目时,哪怕不打算开放Mod,也最好把核心数据格式设计得清晰一些,事后无论做DLC还是做扩展都会省大力气。

5.3 Godot中文乱码:最容易让新人破防的实战坑

再聊一个被问爆的热词:Godot引擎游戏乱码。很多新手第一次在Godot里给Label节点填中文、直接运行,发现显示的全是方块或者乱码,当场心态就崩了。这个坑的根源其实不在Godot本身,而在字体和编码。

第一个原因:Godot的默认字体只带拉丁字符集,不包含中文字形。中文显示成方块,通常不是程序坏了,而是字体里没这个字。解决办法是下载一份开源中文黑体(比如思源黑体或Noto Sans SC),导入项目后创建一个Theme资源,把Default Font设置成这个字体文件,再把项目设置里的GUI主题指向它。这样一来,项目内所有默认控件就都支持中文了。

第二个原因:源文件编码错了。如果你的脚本或CSV文本文件是用中文Windows的GBK/ANSI编码保存的,而Godot要求UTF-8,文字读进来就会全部变成乱码,这种情况通常连英文标点都一同遭殃。做法很简单:所有涉及中文字符串的脚本和数据文件,保存时统一选UTF-8。用VS Code或Godot自带的脚本编辑器重存一遍基本就能解决。

我遇到过很多人卡在这两个问题上一整天,最后发现就是字体没换或者编码没存对。给新手一个固定排查顺序:先看是不是所有中文都变方块,如果是,必然是字体缺字;再看是不是只有特定文件乱码,如果是,必然是编码问题。按这个顺序查,五分钟内就能定位。

6. 从历史里带走的能力:选型判断、调试直觉和一条自学路线

6.1 理解历史决策,等于给你的调试系统开挂

这章聊点相对“玄”但特别值钱的东西。学引擎历史的最大收获,不是记住哪年发生了什么,而是建立起一种“设计决策直觉”。当你理解了固定时间步长是为了让物理不依赖帧率,就不会写出帧率相关的移动代码;当你理解了场景树是层级变换的载体,就不再困惑为什么子节点位置会被父节点带着跑;当你理解了资源加载要显式管理,就会提前防备Texture没释放导致的内存膨胀。

我做过的一个小Demo曾经出现诡异Bug:角色在60Hz屏幕下表现正常,换到144Hz的显示器上直接穿墙飞天。查了一下午,最后定位到是物理更新绑在了渲染帧率上,帧率越高每次物理推进的步长越小,步长再乘上速度累积,最终导致远远一步跨过了碰撞体的厚度。这种“高刷屏加速”现象,在理解了固定步长机制之后就是一眼秒懂的事,根本不用一行行断点去追。类似的经验在游戏开发里数不胜数,而这些直觉全部来自对“引擎为什么这么设计”的理解。

6.2 选引擎前先回答三个问题

结合前面对引擎历史和各种引擎路线的梳理,我建议每个准备立项的团队,先别急着对比哪个引擎的渲染Demo更炫,而是回答三个问题。

第一,你做的到底是什么品类?手游、2D横版、还是次世代3D?品类基本上已经决定了引擎的舒适区。2D和移动端Unity顺手,重3D和大世界UE胜出,小而美或开源项目Godot足够用。

第二,团队的技术栈和规模是什么?团队熟悉什么语言,直接决定学习成本和招聘难度。一屋子C#熟手硬切UE用C++,项目前期推进速度会非常折磨。

第三,授权成本和商业诉求是否匹配?Unreal是超过一定收入开始抽成,Unity按席位订阅,Godot彻底免费。三者没有绝对好坏,但账要提前算清楚。

引擎主力语言擅长领域典型适用场景最需要警惕的点
UnityC#2D、移动端、中轻量3D中小团队商业项目、独立游戏版本碎片化、升级项目时迁移成本高
UnrealC++、蓝图重度3D、大世界、影视级渲染3A级项目、视觉主导的项目C++上手曲线陡峭、硬件门槛高
GodotGDScript、C#2D、轻量3D、原型、教学低成本项目、开源产品、快速原型重型3D生态与工具链还在成长期
自研引擎任意特定品类极致性能平台型大公司、技术驱动团队周期长、招人和维护成本极高

这三个问题你如果心里没数,就算暂时挑了个引擎,做到中期大概率也会因为“不匹配”而返工。选引擎本质上是在选“生产方式的匹配度”,这和历史决策是同一个逻辑。

6.3 动手路线图:写一个微型引擎来给系列热身

作为“原理与实践”系列的开篇,我在结尾埋个可执行的入门任务。强烈建议每个想真正搞懂引擎的人,别只停留在看文章和刷教程,抽一个周末用raylib或SDL2写一个微型的“伪引擎”,下面四步就够了。

第一步,开一个窗口并实现游戏循环:处理退出事件、按固定时间步长更新一次逻辑、立刻渲染当前状态。第二步,实现一个极其简单的场景树:假设有个节点有位置和子节点列表,子节点的最终位置等于父节点位置叠加自身偏移。第三步,用键盘输入控制一个方块移动,加上渲染。第四步,把你往期的经验加进去:试着把一个关卡的地图数据存到外部文本文件里,启动时加载进来。

等你亲手把这几步做完,你会突然看懂了Unity的Hierarchy、Godot的场景树、甚至Doom的WAD文件为什么长那样——因为它们面对的问题是同一个。后面几篇我会拿一个具体的小引擎项目,把游戏循环、场景管理、输入分发、资源加载这些模块一个一个拆开来写。建议你先把这个热身项目跑起来,到时候我们直接在上面加代码。

最后说点个人体会。我刚开始学游戏开发那会儿,抱着Unity一顿操作,教程看了几十个,结果连“为什么一个游戏对象要挂一堆组件而不是直接写一个超大类”都答不上来。直到有一天我老老实实用raylib写了个几百行的小循环,把窗口初始化、帧循环、变换、碰撞全部亲手敲了一遍,再回头看Unity的场景面板,瞬间有一种捅破窗户纸的感觉。历史的逻辑一直原原本本地摆在今天每个编辑器的底层里,只是平时很少有人带你去看它。这个系列既然开了头,我希望能陪你一层层把那层窗户纸捅开。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询