ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

游戏引擎是什么?一文读懂工作原理、主流引擎与选型建议

游戏引擎是什么?一文读懂工作原理、主流引擎与选型建议 从哪年开始玩游戏的估计很多人跟我一样最早接触“游戏引擎”这个词是在各种游戏启动画面里——Unity的图标、Unreal的图标或者打开某些游戏目录时看到的一堆DLL文件。那时候我根本搞不懂引擎到底是啥是软件是代码库还是像发动机一样的东西后来自己动手做过几个小游戏才慢慢摸清楚引擎其实就是一套帮你“造游戏”的工具箱或者说是一套管理复杂游戏世界的操作系统。这篇是“游戏引擎原理与实践”系列的第一篇我想先把最基础的东西聊透——游戏引擎是怎么来的现在变成了什么样以及你作为一个游戏开发者、Mod制作者或者只是好奇的玩家该如何理解它。这一篇不会让你直接写出一个引擎但会帮你建立一张完整的地图以后再接触任何引擎都不会发怵。1. 游戏引擎到底是什么——先拆掉那层神秘面纱1.1 引擎的本质一套“造游戏的工具箱”如果你没写过游戏可能会觉得引擎是一种高深莫测的“程序”。但你可以把它想象成装修房子你要装水电、铺地板、刷墙、放家具如果每间房都从零开始找材料、自己做砖头那没个几年搞不完。引擎就等于提前给你准备好了水电管网、预制地板、标准五金件你只需要关心“怎么设计这个房间”而不是“怎么炼钢”。放到游戏里引擎解决的是所有游戏都会面临的共性问题怎么把图片和模型画到屏幕上怎么处理玩家键盘鼠标输入怎么让两个物体发生碰撞怎么播放声音怎么管理游戏里的几十个关卡这些事如果每一款游戏都从头写一遍那游戏行业早完蛋了。引擎把这些共性能力抽出来变成一套可复用的代码库和编辑器工具开发者只需要专注于游戏特有的玩法逻辑。很多初学者会混淆“引擎”和“游戏本身”。其实你玩到的《塞尔达》《原神》是游戏而承载它们运行的底层框架才是引擎。最简单的验证方式同一个引擎可以做出完全不同的游戏。Unity引擎上既有《空洞骑士》这种2D动作游戏也有《原神》这种大型3D开放世界还有一堆休闲手游。这就是引擎的“通用性”——它是游戏的上层建筑而不是某个具体游戏本身。1.2 一个典型引擎的模块地图不管什么引擎核心模块其实大同小异。我习惯把引擎拆成几个“器官”这样理解起来特别清晰渲染模块负责把3D模型、贴图、光影转换成屏幕上的像素。这是引擎里最复杂也最炫酷的部分DirectX、OpenGL、Vulkan这些图形API就是它跟显卡对话的“翻译官”。物理模块负责模拟重力、碰撞、刚体运动。现在引擎里的物理系统已经进化到可以模拟布料、头发、流体甚至破坏效果。音频模块处理声音播放、3D音效定位、混音。别小看它恐怖游戏的氛围有一半靠声音。输入模块把键盘、鼠标、手柄、触屏的操作统一成抽象指令让开发者不用关心玩家用的是Xbox手柄还是PS5手柄。资源管理模块负责加载模型、贴图、音频、配置文件还要管内存释放、流式加载。大型开放世界的地图能无缝加载全靠它。场景管理模块管理游戏世界里的所有对象。你在编辑器里摆放的每一个敌人、宝箱、NPC都靠这个模块记录位置和状态。脚本系统现在几乎所有引擎都内置了脚本语言C#、Lua、GDScript等让策划和程序员能快速写玩法逻辑不用重新编译引擎本体。编辑器很多老派引擎没有独立编辑器但现代引擎标配一套可视化工具用来拖拽场景、调整参数、预览效果。这也是Unity和Unreal能普及的重要原因。了解这些模块后你就明白了所谓“游戏引擎原理”本质就是搞懂这些器官如何协同工作。后面几篇文章我们会挨个拆解今天先走马观花。2. 游戏引擎的前世从“没有引擎”到“上古引擎”2.1 没有引擎的年代代码即游戏上世纪70到80年代初游戏开发完全是另一副模样。那时候没有“引擎”这个概念每个游戏都是一坨巨大的、不可拆分的代码。你写一个《太空侵略者》那么“移动飞船”“发射子弹”“判断碰撞”“绘制画面”这些逻辑全部搅在一起——甚至因为当时的硬件太弱开发者还得用汇编语言逐行操作显存换算成现实就是你装修房子要自己烧砖、自己发电、自己铺路房子和施工工具没有任何分离。这个时期的游戏开发一个游戏就是一个独立的可执行文件换一个游戏就得重写90%的代码。更糟的是代码极度依赖特定硬件。在雅达利2600上跑得好好的游戏移植到其他主机上基本等于重做。所谓的“移植”不是改改参数而是把整个程序的底层重新折腾一遍。现在听起来像地狱难度但当时的人不知道反而觉得理所当然——因为还没有人想到“游戏开发应该有个可复用的底座”。直到80年代中期一些商业公司开始意识到如果有一套通用的图像绘制、输入处理库多个游戏项目就能共享同一个技术底座。这就是引擎的雏形。比较有代表性的是1982年发布的Pinball Construction Set它让玩家通过拼搭的方式制作弹球游戏已经带点“引擎编辑器”的味道但它更像玩具不是真正给开发者用的引擎。真正的转折点要等id Software出现。2.2 引擎概念的萌芽id Software与“可复用”的革命1993年id Software发布了《DOOM》。这款游戏在技术和商业两个层面都带来了“引擎”概念的真正爆发。当时id的开发者约翰·卡马克和迈克尔·亚伯拉什写出了一个极其高效的软渲染器能在普通PC上流畅渲染3D视角的地牢场景。更重要的是卡马克做了一个至今影响深远的决定把游戏核心玩法逻辑敌人AI、关卡触发、物品拾取和底层渲染代码分离然后用一套“数据文件”WAD文件来存放关卡和资源。这意味着什么意味着玩家和第三方开发者可以不碰游戏源代码只靠修改WAD文件里的地图和贴图就能做出全新的关卡甚至全新的游戏。于是“DOOM引擎”首次变成了一个独立于游戏本身的“产品”。1994年的《Heretic》就是用DOOM引擎做的另一个游戏这直接证明了“引擎可复用”的价值。后来id继续推出《Quake》系列Quake引擎引入了真正的全3D渲染、客户端/服务器网络架构还支持用C语言写游戏逻辑这已经非常接近现代引擎的形态。同时期还有Build引擎它做出来的《毁灭公爵3D》也风靡一时但技术上比Quake差一代。不管怎样90年代中期的局面是引擎从“代码的附属品”变成了“代码的核心资产”引擎授权甚至可以单独卖钱。约翰·卡马克后来还干了一件载入史册的事把id引擎的源代码逐步开源从Quake到Doom 3让全世界的开发者都能学习真实引擎的实现。今天的自研引擎开发者很多人都是读着id的开源代码长大的。2.3 商业引擎的黎明从RenderWare到Unrealid的引擎虽然可以授权但主要是“内部使用附带授权”不是真正的商业引擎产品。真正的商业引擎时代要感谢Epic Games和Crytek这批公司。1998年Epic Games发布了《Unreal》这款游戏本身成功但更成功的是它的引擎——Unreal Engine 1。当时Epic不仅把引擎卖给其他开发商还配套了可视化编辑器UnrealEd这在当时极其罕见。后来《汤姆·克兰西的细胞分裂》就是基于UE2开发的这是一个“别的公司用你的引擎做了完全不同的游戏”的典型案例。与此同时英国公司Criterion Software的RenderWare引擎也走了一条很有意思的路它不做一个完整的编辑器而是提供一套组件库游戏公司可以按需组合。GTA3和《侠盗猎车手》系列前几作就是基于RenderWare你可以看到这套引擎在开放世界方面的早期探索。商业引擎之所以能跑通核心原因是游戏开发成本开始失控。到了PS2/Xbox时代一个3A游戏的开发团队动辄上百人技术攻坚周期太长。买一套成熟的商业引擎能省掉渲染、物理、音频这些“通用部分”的开发时间让团队专心做玩法和美术。这个逻辑一直延续到今天Unity和Unreal能横扫市场本质就是把“引擎”做成了像水电一样的标准基础设施。3. 游戏引擎的今生三大流派与当代技术格局3.1 商业引擎的统治Unity与Unreal的分工现在说到游戏引擎绕不开Unity和Unreal虚幻。它们是商业引擎的两大巨头但走的路子完全不同。Unity的核心优势是“轻量、易上手、跨平台”。它的脚本语言是C#编译运行机制让开发者可以快速迭代编辑器界面友好资产商店生态极强。因此中小团队、独立开发者、手游团队非常偏爱它。你玩到的很多手游比如《王者荣耀》早期版本、一些二次元手游都有Unity的身影。Unity的弱项在于超大场景的渲染质量和部分高端特性的打磨但它在2D游戏、UI交互、跨平台发布上的成熟度是顶级的。Unreal的核心优势是“画质天花板、重度3D”。从UE3开始Epic就主打高端渲染UE4推出时甚至免费开放源码只在游戏盈利后收取分成。UE的蓝图可视化脚本系统让它有了一个很独特的学习曲线非程序员可以通过拖拽节点做玩法逻辑程序员可以用C深入底层。目前大量3A大作、电影级游戏、虚拟制片项目都在用UE5尤其是Nanite和Lumen两套技术出来后引擎的表现力已经逼近电影渲染。缺点也很明显上手难度高、打包体量大、对硬件要求高。两个引擎的竞争关系像Windows和macOS——没有绝对的优劣只有适配的场景。作为新手不需要一上来就纠结“哪个最强”而要问“哪个更适合我现在的项目”。后面第5章我会给选型建议这里先不展开。3.2 开源引擎的崛起Godot、O3DE与社区力量商业引擎之外开源引擎在近十年里强势崛起最典型的就是GodotGodot Engine。Godot最大的卖点有两个一是完全开源免费采用MIT协议商用没有任何版权费二是全功能覆盖但体积极小安装包只有几十MB却包含2D/3D渲染、物理、动画、粒子、脚本、编辑器等完整功能。它的2D渲染管线在业界很受好评很多独立开发者的像素游戏、平台跳跃游戏都选择Godot。它的内置脚本语言GDScript语法接近Python对新手极其友好。另外Godot 4.0以后的3D渲染器也提升到了新的水平加上Unity和Unreal的收费政策变动近年来很多中小团队从Unity转向了Godot。开源引擎里另一个大块头是O3DE它是亚马逊在Lumberyard基础上改造的引擎主打高保真3D和云端游戏但生态还不成熟普通开发者不建议尝试。还有不少小众引擎比如开源界的OGRE更偏向渲染库、Stride等。开源引擎的意义不仅在于免费更在于“代码可读”——你可以看到引擎内部的实现细节这对学习游戏引擎原理来说是无价之宝。我个人强烈建议每个想深入了解引擎的人至少读一读Godot的源码或文档因为它足够精简、清晰、没有商业包袱。3.3 自研引擎的坚持为什么大厂还要自造轮子既然商业引擎这么强大为什么CD Projekt Red、育碧、Rockstar这些大厂还要花巨额成本自研引擎原因有几个一是“可控性”。商业引擎的路线图掌握在别人手里如果Epic突然改版或调整收费策略你所有项目都会被动。二是“差异化”。某些游戏的玩法需求非常独特通用引擎没办法高效支持。比如《荒野大镖客2》那种极致拟真的马匹物理和AI调度或者是《怪物猎人世界》里复杂的部位破坏和怪物生态都需要引擎底层深度定制。三是“性能榨干”。主机平台的内存和CPU资源极其有限自研引擎可以针对特定硬件做极致优化而通用引擎为了兼容所有平台很难做到面面俱到。自研引擎的代表有很多Rockstar的RAGE引擎、任天堂的各种内部引擎、EA的Frostbite寒霜、动视的IW引擎使命召唤系列。这些引擎经过十几年甚至几十年的迭代已经与自家游戏深度绑定。问题的另一面是自研引擎的开发成本极其高昂而且新手很难接触。所以如果你想深入学习引擎原理还是从开源引擎或商业引擎入手更现实。自研引擎的“乐趣”在于让你明白整个行业是怎么回事但你未必需要去造一个。3.4 引擎与模组加载器以BepInEx为例聊聊运行时聊到现代游戏引擎就不得不提它们与游戏模组Mod的关系。很多玩家喜欢给游戏加Mod而加Mod的关键就是要找到游戏引擎的“运行时入口”。这里就引出一个经常在社区里被问到的词BepInEx。BepInEx是一个游戏模组加载器它做的事情本质上是在游戏启动的时候“抢先注入”一个插件环境让Mod代码能够被加载执行。BepInEx能注入哪些游戏引擎答案是它主要面向使用Mono或.NET运行时构建的游戏其中最典型的引擎就是UnityMono后端此外也包括一部分使用.NET框架自研引擎的游戏。很多Unity游戏比如《雨中冒险2》《英灵神殿》《缺氧》等都能通过BepInEx挂载插件。原因在于Unity的Mono脚本运行时提供了反射和程序集加载机制BepInEx抓住这个入口就能在游戏进程里插入自己的代码。这里其实涉及一个重要的引擎原理概念脚本运行时Scripting Runtime。Unity用Mono或IL2CPP来托管C#代码。Mono后端很容易被注入因为游戏逻辑以.NET程序集DLL形式存在运行时允许加载额外程序集。BepInEx就是利用了这一点。如果你玩的游戏是基于IL2CPP后端Android上尤其常见注入就复杂得多需要更底层的钩子技术。这告诉我们理解引擎的脚本运行时体系对做Mod、做工具链、甚至做游戏安全的人来说都极其关键。后面第4.4节我会给一个简单的BepInEx实操演示。4. 引擎核心原理拆解与一次小型实践4.1 主循环所有引擎跳动的“心脏”不管你用什么引擎内核都有一个“主循环”Game Loop。它长这样处理输入读取键盘、鼠标、手柄状态。更新逻辑根据输入更新游戏状态玩家位置、敌人AI、血量、事件。渲染画面把更新后的场景画到屏幕上。回到第一步每帧循环。现代引擎基本都朝这个结构演化但细节上有两个关键参数帧率FPS和DeltaTime。DeltaTime就是每一帧消耗的时间。如果游戏能稳定跑60FPS那DeltaTime约0.0167秒。物理和逻辑更新都要乘以DeltaTime这样不管帧率高低游戏速度都一致。否则高配电脑上游戏跑得像火箭低配电脑上慢得像慢动作。如果你自己写引擎主循环的框架通常放在一个叫“Application”或“Engine”的类里伪代码大概是while (running) { float deltaTime calculateDeltaTime(); inputSystem-update(); gameLogicSystem-update(deltaTime); physicsSystem-update(deltaTime); renderSystem-render(); }Unity的Update()方法、Godot的_process(delta)方法其实就是在每一帧的主循环里被引擎框架自动调用的钩子。你做的所谓“用引擎做游戏”大部分时候是在往引擎预留的钩子里填逻辑。明白这个以后看到“帧率”“卡顿”“deltaTime”这些词你就会有直觉反应。4.2 组件系统与场景图游戏世界的组织方式现代引擎普遍使用“实体-组件”Entity-Component架构。什么意思呢传统面向对象里你会设计一个Player类里面有位置、模型、血量、碰撞盒但如果要加一个NPC你可能要复制很多代码。而组件架构把游戏对象拆成一个个“组件”——Transform变换、MeshRenderer网格渲染、Collider碰撞器、Health血量、AI等。游戏里的每个物体都只是一个空壳子上面挂不同组件组合出不同行为。这就像搭乐高一个角色就是“身体腿头武器”的组合而不是“一个完整雕塑”。Unity里的GameObject Component、Godot里的Node Node2D/Script本质都是这个思路。这种架构的最大好处是复用和组合你可以用同样的组件拼出NPC、敌人、道具省去大量重复代码也方便引擎做可视化编辑。场景图Scene Graph则是描述对象层级关系的树结构。比如一个“角色”节点下面挂一个“武器”子节点武器会跟着角色一起移动。Godot特别依赖场景树Unity叫做Transform层级。理解场景树你就理解了编辑器里的Hierarchy面板到底是什么——它是一棵对象树根节点是场景子节点继承父节点的变换关系。这棵树不仅是组织方式也决定了渲染顺序、事件传递和内存管理。很多新手第一次做游戏时把所有东西都摆在同一个节点下结果动画、碰撞到处出错就是因为没理解父子关系带来的隐式继承。4.3 实操在Godot里解决中文乱码问题现在聊一个非常具体、且新手经常撞墙的问题Godot引擎游戏中文乱码。这问题是有人搜到的最新的网络热词我一开始也遇到过心里那叫一个急。Godot乱码的根源通常有三种默认字体不支持中文Godot默认字体只有拉丁字符不含CJK中日韩字形所以你直接输出中文时编辑器里可能正常但运行时显示成方框或乱码。资源文件编码问题脚本文件不是UTF-8编码导致字符串被错误解析常见于Windows下用记事本另存为ANSI编码的情况。动态字体回退Font Fallback没配置Godot 4支持字体回退链如果你的主字体缺某个字形引擎会去下一个字体里找没配置就会显示“豆腐块”。针对这三种情况解决方案如下如果是默认字体问题你需要导入一个中文字体文件比如思源黑体、阿里巴巴普惠体注意开源许可。在Godot里创建FontFile资源然后在控件的Theme里设置字体或者全局设置Project Settings - GUI - Theme - Default Font。如果是脚本编码问题确保脚本文件以UTF-8保存。Godot 4中推荐在脚本顶部加# coding: utf-8虽然GDScript默认就是UTF-8但某些外部编辑器可能改写编码。如果是字体回退问题在Godot 4中你可以在字体资源的Fallbacks数组里添加一个支持中文的字体这样英文保持主字体风格中文自动回退到备用字体。举一个简单的设置示例在GDScript中动态加载字体并设置给Labelextends Label func _ready(): var font_db FontFile.new() font_db.load_dynamic_font(res://assets/fonts/SourceHanSansSC-Regular.otf) add_theme_font_override(font, font_db)当然这只是一个入门解法。更完整的方式是在编辑器里把字体资源拖到Label的Theme Overrides里。要注意的是打包发布后字体文件也要包含在导出资源中别漏了。踩过这个坑之后我养成一个习惯写任何游戏界面文本前先确认项目里配好了完整的中文字体和编码规范。4.4 实操用BepInEx体验一次引擎级Mod注入前面聊了理论现在来一个实际体验BepInEx的机会。如果你正好有支持BepInEx的Unity游戏比如上面提到的几款可以试试给游戏加一个最简单的Mod感受一下引擎运行时的“缝隙”。操作流程分几步下载BepInEx去BepInEx官方GitHub Releases下载对应游戏架构的版本通常选x64版。安装把BepInEx压缩包里的文件直接解压到游戏根目录要求游戏目录里能看到BepInEx文件夹和winhttp.dll或doorstop相关文件。启动一次游戏让BepInEx初始化生成plugins、config等目录。写一个插件在BepInEx/plugins目录下新建一个.cs类型的插件项目实际上BepInEx插件通常先编译成DLL然后在plugins文件夹里放DLL。你可以用Visual Studio或Rider创建一个.NET类库引用BepInEx.dll再写类似这样的代码using BepInEx; using UnityEngine; [BepInPlugin(com.example.helloworld, HelloWorld, 1.0.0)] public class HelloWorldPlugin : BaseUnityPlugin { private void Awake() { Logger.LogInfo(Hello from BepInEx!); } private void Update() { if (Input.GetKeyDown(KeyCode.F1)) { Logger.LogMessage(F1 pressed!); } } }编译并运行把生成的DLL复制到BepInEx/plugins目录再启动游戏。如果看到控制台或日志输出“Hello from BepInEx!”就说明注入成功了。注意并不是所有Unity游戏都能直接被BepInEx注入。部分使用IL2CPP后端、或者加了反作弊保护的游戏比如需要联机对战的手游会冲突。做Mod前先查游戏社区里是否已有BepInEx示例。说实话这种玩法最大的意义不在于改游戏而在于让你直观感受到“游戏程序在运行时”“引擎的脚本层可以被外部代码触达”——这个认知对做游戏开发、做外挂防护、做工具链都有帮助。5. 新手选型建议与避坑指南5.1 Unity、Unreal、Godot怎么选很多零基础的朋友上来就问“学哪个引擎好”。我的回答永远是一句话先看你想做的游戏类型再看你手里的时间最后才是引擎本身。如果做2D独立游戏、休闲手游、工具类应用Godot是最轻快的选择。它的场景树和信号机制很直观GDScript学起来也快我个人用它做过小项目从下载到出Demo只花了一个周末。当然Godot的3D能力在UE面前还是弟弟但如果你做像素风、平面叙事类游戏完全够用。如果做商业手游、跨平台小团队项目Unity更稳。它的生态最成熟教程多、插件多、招聘需求也大。国内很多大小厂招聘技术美术和客户端开发时都要求Unity。考虑就业的话Unity是个不错的起点。如果想做3A级画质、硬核动作游戏、开放世界或者想进大型主机项目Unreal是首选。它的蓝图系统很适合美术和策划入门C部分则需要较深功底。但UE的上手门槛确实高引擎概念多出包慢很多新手学了几个月还在跟材质节点搏斗。不同引擎对比维度GodotUnityUnreal上手难度很低中等较高2D支持优秀良好一般3D画质中等良好顶级脚本语言GDScript/C#C#C/蓝图开源免费完全免费MIT订阅制有免费版免费盈利分成主要适用独立开发者/小团队移动端/中小企业3A/大团队5.2 学习路径与常见误区学习游戏引擎最容易犯的第一个错是“死磕原理不动手”。很多朋友买了很多解析引擎源码的书结果连一个完整的小游戏都没做过。我的建议是“先做出一个极小的游戏再去读原理”。哪怕是一个“小球吃掉十个点就胜利”的Demo也能让你体验完整流程场景搭建、脚本编写、碰撞检测、UI、打包发布。做完这遍你再看引擎原理才能真正对上号。第二个误区是“只看不做看教程比写代码还多”。游戏引擎是实践性极强的工具看十小时教程不如自己敲两小时代码。遇到不会的先想到查官方文档再看社区帖子最后才问人。第三个误区是“一把梭学所有引擎”。很多新手Unity学几周又跑去学Unreal最后哪个都半吊子。建议选定一个引擎用三个月时间做完至少两个小项目。这之后你再去碰第二个引擎会发现很多概念都是相通的——渲染、物理、动画、UI、网络背后的原理都来自同一套计算机图形学和实时交互逻辑。5.3 引擎学习的三条心得写到这里忍不住分享三条我实操中沉淀下来的经验。第一条把引擎当“黑盒白盒”交替理解。一开始你只需要关心引擎提供的能力不需要知道它怎么实现。比如碰撞检测你只需要知道“给物体加Collider组件它就能被碰撞检测到”。但当你在开发中遇到性能瓶颈比如大量物体物理碰撞导致掉帧你就要打开“白盒”去了解碰撞检测的空间划分算法比如Godot或Unity里的网格划分。黑盒让你快速产出白盒让你进深优化。第二条一定要养成读Log的习惯。很多新人看到控制台报错就慌。游戏引擎本身不会“坏”报错98%来自你的代码逻辑或者资源配置。耐心的做法是看第一行错误信息搜错误码去引擎文档查看对应API的用法。我解决过无数次“乱码”“黑屏”“物体消失”问题最终都是靠读Log找到思路而不是靠猜。第三条千万别滥用外部插件。BepInEx、各种商店插件能提升开发效率但你如果不理解底层机制一旦出问题就是灾难。比如我之前用过某个商店插件跟引擎版本一升级就崩溃查了半天发现插件依赖的API被弃用。引擎本身在飞速演进插件往往跟不上。所以能用引擎原生能力实现的功能尽量别堆插件。最后再分享一个小技巧如果你真的想透彻理解引擎最有效的办法并不是从Unity/Unreal这种巨型引擎入门而是先用2D引擎比如Godot做小项目然后去看一些极简引擎的源码例如Fix Your Timestep系列文章或者用SDL写一个几百行代码的小游戏框架。当你亲手敲过一个主循环、手工处理过输入和渲染再回到商业引擎你会觉得那些神秘功能都变得透明了。游戏引擎从过去的一图谱代码走到现在的工业化工具本质上就是“抽象”和“复用”不断进化的故事。理解它的前世今生不是为了背历史而是为了帮你在这个领域找到自己的位置——你是玩家、Mod作者、独立开发者还是引擎工程师都没关系从这一篇开始你对引擎的认识已经领先了别人一大截。
返回列表