ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

游戏引擎从零到一:核心模块拆解与主流引擎选型指南

游戏引擎从零到一:核心模块拆解与主流引擎选型指南 1. 游戏引擎到底是个什么东西先把话说直白点游戏引擎就是一套“做游戏的工具箱加流水线”。你玩到的每一款游戏画面怎么渲染出来、角色怎么动起来、物理碰撞怎么算、声音什么时候响、资源怎么加载背后都靠这套东西在撑着。没有引擎开发者就得从零去写图形接口、写内存管理、写碰撞检测那基本等于每次做游戏都先造一台电脑。我接触引擎是从大学时候折腾一个2D小游戏开始的。当时觉得“引擎”这个词特别玄乎后来真正拆开看才明白它本质上就是一堆模块的集合渲染模块负责把三角形画到屏幕上物理模块负责算物体怎么掉、怎么撞音频模块负责混音和播放资源模块负责把图片、模型、音频从硬盘读进内存脚本模块负责让策划和程序能快速写逻辑。这些模块各司其职又通过一套统一的接口串起来最终让开发者能专注于“游戏好不好玩”而不是“三角形怎么画”。那为什么现在大家张口闭口都是Unity、Unreal、Godot因为引擎发展到今天已经不只是工具了它变成了一种生态。你用Unity意味着你能直接买到Asset Store里的资源能招到会C#的人能查到海量教程你用Unreal意味着你能拿到顶级的渲染效果能直接用蓝图连逻辑能蹭到Epic的技术支持。引擎的选择某种程度上决定了你项目的开发效率、团队构成和最终品质上限。这一篇作为系列的开头我不打算一上来就讲代码或者讲某个具体引擎的API而是想先把“游戏引擎的前世今生”这条线捋清楚。你只有知道它从哪来、为什么长成现在这样后面学具体模块的时候才不会觉得知识点是散的。这篇文章适合刚入行的新人、想转行做游戏开发的程序员也适合做了几年业务但没系统梳理过引擎架构的老手。我会从引擎的起源讲起一路聊到现在的格局中间穿插一些我自己的踩坑经验和行业观察尽量让不同基础的人都能看懂。2. 从“没有引擎”到“引擎”的进化史2.1 早期游戏每款游戏都是一次从零开始上世纪七八十年代的游戏开发跟现在完全是两码事。那时候没有“引擎”这个概念一款游戏就是一个完整的程序从画面到逻辑全部写死在一起。比如雅达利上的《Pong》整个游戏可能就几百行汇编球怎么动、板怎么挡全是硬编码。你想改个规则对不起得把代码翻出来重新写。这种模式的问题很明显复用性极差。今天做完一个打砖块明天要做个赛车除了最底层的硬件操作几乎没有任何代码能直接搬过去。每个项目都在重复造轮子而且造出来的轮子还都不一样。我后来看一些老游戏的源码发现他们连“把像素画到屏幕”这种基础操作都要在每个游戏里重新实现一遍放到今天简直不可想象。但那个年代也有它的合理性。硬件性能极其有限内存按KB算CPU主频按MHz算你根本没有余力去做抽象层。每一行代码都要榨干硬件的最后一点性能所以“专用”反而比“通用”更划算。这就像你开一家小面馆没必要去买一套中央厨房设备一口锅一把刀就够了。2.2 引擎雏形的出现id Software的启示真正让“引擎”这个概念浮出水面的是上世纪九十年代初的id Software。他们做《Wolfenstein 3D》的时候把渲染、地图、逻辑做了一定程度的分离到了《Doom》这套分离更加彻底。Doom的关卡数据是独立于程序之外的玩家甚至能自己制作地图WAD文件这在当时是革命性的。我印象最深的是《Quake》的诞生。id Software做了一个非常大胆的决定把渲染部分和游戏逻辑部分通过一套网络协议彻底解耦。这意味着什么意味着你可以换一套游戏规则但渲染和网络层不用动。这就是引擎思想的雏形——把“通用的部分”抽出来把“变化的部分”留给游戏本身。后来id Software把Quake的引擎授权给别人用出现了《Half-Life》这样的作品。Half-Life用的就是改过的Quake引擎但它的游戏逻辑、关卡设计、叙事方式完全是自己的。这证明了引擎授权的商业模式是可行的我卖你一套底层技术你在上面做你的游戏大家各取所需。2.3 商业引擎的崛起Unity和Unreal的双雄时代进入21世纪引擎开始从“大厂自研自用”走向“商业化对外授权”。最早做这件事的是RenderWare、Gamebryo这些但真正把市场做大的是Unity和Unreal。Unreal Engine 1在1998年发布当时主要是给FPS游戏用的。但Epic很聪明他们不断迭代到UE3的时候已经能支持各种类型的游戏了。UE4在2014年推出带着蓝图可视化脚本和PBR渲染直接把门槛拉低了一大截。我记得第一次打开UE4编辑器的时候那种“所见即所得”的震撼是很强烈的——你拖一个光源进去场景立刻亮起来改一个材质参数模型表面马上变化。这种即时反馈对开发者来说太重要了。Unity的路线不太一样。它2005年才发布一开始主打Mac平台后来靠移动端爆发。Unity的特点是“轻”和“灵活”C#脚本写起来快编辑器操作简单2D和3D都能做。我身边很多独立开发者都是从Unity入门的因为它的学习曲线确实比UE平缓。但Unity早期也有问题比如渲染效果不如UE大型项目的管理比较混乱这些年在慢慢补齐。现在的情况是Unity和Unreal占据了大部分市场份额但Godot、CryEngine、Lumberyard现在叫Open 3D Engine等也在各自领域有一席之地。特别是Godot开源免费轻量级最近几年增长很快。不过Godot也有自己的问题比如中文资料相对少某些平台的兼容性还需要打磨网上偶尔能看到“godot引擎游戏乱码”这类反馈多半是字体资源或者编码设置没弄对后面我会专门聊这个。3. 引擎的核心模块拆解一台机器是怎么转起来的3.1 渲染管线从三角形到屏幕像素渲染是引擎里最复杂也最核心的模块。你看到的每一帧画面背后都经历了一条长长的管线顶点数据从内存传到GPU经过顶点着色器变换到屏幕空间再经过光栅化变成一个个像素最后在像素着色器里计算颜色输出到帧缓冲。这条管线里每一步都有讲究。比如顶点着色器里要做MVP变换模型-视图-投影矩阵这三个矩阵怎么算、怎么乘直接决定了物体在屏幕上的位置对不对。我刚开始学的时候经常搞混顺序把投影矩阵乘在视图矩阵前面结果画面完全乱掉。后来才明白矩阵乘法不满足交换律顺序错了结果就错了。再比如光栅化阶段GPU要把三角形覆盖的像素找出来这个过程涉及到采样和插值。如果三角形很小采样点不够就会出现锯齿如果三角形很大插值计算量就上去了。所以引擎里会有各种抗锯齿技术MSAA、FXAA、TAA本质上都是在解决“采样不足”这个问题。像素着色器里做的事情就更多了。光照计算、纹理采样、阴影映射、后处理效果全在这一步完成。PBR基于物理的渲染是现在的主流它用一套统一的公式来描述不同材质的反射行为金属、塑料、布料都能用同一套参数表达。我实测下来PBR材质在UE和Unity里的表现差异主要来自光照模型和后期处理的调校底层原理是相通的。3.2 物理引擎让世界遵守牛顿定律物理模块负责模拟现实世界的运动规律。刚体、碰撞体、关节、力场这些概念构成了物理引擎的基本骨架。你角色走路会撞墙子弹打出去会下坠箱子堆起来会倒塌全靠物理引擎在算。物理引擎的核心是碰撞检测和碰撞响应。碰撞检测要判断两个物体有没有相交常用的方法有AABB包围盒、OBB包围盒、GJK算法等。AABB最简单但精度低OBB更贴合物体形状但计算量大GJK能处理凸体但实现复杂。引擎通常会根据物体类型和精度需求选择不同的检测方法。碰撞响应则是算“撞了之后怎么办”。弹性碰撞、非弹性碰撞、摩擦力、恢复系数这些参数决定了物体撞完之后是弹开还是停住。我做过一个demo两个球对撞恢复系数设0.9的时候弹得很高设0.1的时候几乎不弹。这个参数在游戏里很关键比如做台球游戏恢复系数要接近1做角色落地恢复系数要接近0。物理引擎还有一个大坑是“穿透”。当物体速度很快或者帧率很低的时候物体可能在一帧之内穿过另一个物体碰撞检测就漏掉了。解决办法有连续碰撞检测CCD、子步进substep等。我在做一个高速子弹的项目时就遇到过这个问题子弹打出去直接穿墙后来开了CCD才解决。3.3 资源管理内存里的搬运工资源管理听起来不起眼但它是引擎里最容易出性能问题的地方。纹理、模型、音频、动画、脚本这些资源怎么加载、怎么缓存、怎么释放直接决定了游戏的加载速度和运行流畅度。最基础的是同步加载用到什么就加载什么加载完再继续。这种方式简单但会卡顿。你打开一个宝箱里面有个新武器如果同步加载武器模型画面就会停一下。所以现代引擎都用异步加载在后台线程把资源读进来主线程继续跑等资源准备好了再替换。资源缓存也很重要。同一个纹理被多个物体引用如果每个物体都加载一份内存就爆了。所以引擎里会有引用计数或者资源池确保同一份资源只存在一份。我见过一个项目因为没做资源缓存同一个角色的贴图被加载了上百次内存直接飙到几个G后来加了缓存才降下来。还有资源的生命周期管理。什么时候释放引用计数归零的时候释放还是等场景切换的时候统一释放这取决于引擎的设计。Unity用的是引用计数加自动垃圾回收Unreal用的是更手动的管理方式。各有优劣Unity省心但可能有GC卡顿Unreal可控但容易漏释放。3.4 脚本系统让策划也能写逻辑脚本系统是引擎里最“接地气”的模块因为它直接面向游戏逻辑。策划想改一个数值、加一个技能、调一个关卡如果每次都要程序改C代码再重新编译那效率太低了。所以引擎会提供脚本层让非程序员也能参与开发。脚本系统的实现方式有好几种。一种是嵌入脚本语言比如Lua、Python、JavaScript引擎提供绑定接口脚本调用引擎功能。另一种是可视化脚本比如Unreal的蓝图、Unity的Bolt用节点连线的方式表达逻辑。还有一种是引擎自研的脚本语言比如Godot的GDScript语法简单和引擎深度集成。我个人的经验是可视化脚本适合做原型和简单逻辑复杂逻辑还是得写代码。蓝图连多了之后节点图会变得极其庞大维护起来很痛苦。我见过一个项目一个技能逻辑用了三百多个蓝图节点后来改需求的时候没人敢动那张图。所以我的建议是原型阶段用可视化脚本快速验证正式开发时把核心逻辑用代码重写。4. 主流引擎的选型逻辑与实操对比4.1 Unity、Unreal、Godot的定位差异选引擎这件事没有“最好”只有“最合适”。我做过一个简单的对比表把三个主流引擎的关键维度列出来方便你根据自己的项目需求来判断。维度UnityUnrealGodot授权模式免费订阅免费分成完全开源免费主要语言C#C/蓝图GDScript/C#渲染效果中等偏上顶级中等2D支持好一般很好移动端优秀一般一般学习曲线平缓陡峭平缓社区资源极多多中等适合项目手游、独立、跨平台3A、FPS、高画质独立、2D、轻量Unity的优势在于“什么都能做什么都不差”。手游、独立游戏、AR/VR、工业仿真它都能覆盖。C#写起来舒服编辑器操作直观Asset Store资源丰富。但它的渲染效果确实不如UE大型项目的管理也比较容易乱。Unreal的优势在于“画质天花板”。Nanite、Lumen、MetaHuman这些技术让它在高画质领域几乎没有对手。蓝图系统对策划很友好C源码开放想改底层也能改。但它的学习曲线确实陡C的复杂度、编译时间、项目体积都是需要权衡的。Godot的优势在于“轻量和自由”。完全开源没有授权费没有分成代码随便改。GDScript语法简单2D支持很好编辑器启动快。但它的生态还在建设中中文资料相对少某些平台的兼容性需要自己踩坑。比如前面提到的“godot引擎游戏乱码”很多时候是因为字体文件没有包含中文字形或者文本编码设置不对换一个支持中文的字体、把编码统一成UTF-8基本就能解决。4.2 从零搭建一个最小引擎需要哪些步骤如果你想知道引擎到底怎么运作最好的方式是自己动手搭一个最小版本。不需要做得多完整能把一个三角形画到屏幕上就算成功了一半。第一步是窗口和上下文创建。用GLFW或者SDL创建一个窗口然后初始化OpenGL或者Vulkan的上下文。这一步的目的是让GPU知道“我要往这个窗口里画东西”。第二步是着色器编译。写一个最简单的顶点着色器和片段着色器顶点着色器把顶点坐标传出去片段着色器输出一个固定颜色。编译着色器的时候要注意错误处理GLSL的编译错误信息有时候很隐晦我踩过好几次坑后来养成了每次编译都检查日志的习惯。第三步是顶点缓冲和绘制。把三角形的三个顶点数据传到GPU的显存里然后调用绘制命令。这一步的关键是理解VAO、VBO、EBO这些概念它们决定了GPU怎么读取你的顶点数据。第四步是主循环。一个典型的游戏循环是处理输入、更新逻辑、渲染画面、交换缓冲。这个循环每秒跑60次就是60帧。循环里要注意时间管理用deltaTime来控制逻辑更新速度避免不同帧率下游戏速度不一致。第五步是资源加载。写一个最简单的纹理加载器把一张图片读进来绑定到着色器上。这一步会涉及到图像格式、纹理过滤、Mipmap等概念每一个都有讲究。这五步做完你就有了一个能画三角形、能贴纹理的最小引擎。虽然离“能做游戏”还差得远但至少你知道了引擎的骨架长什么样。后面再往上加物理、音频、脚本就是往这个骨架上填肉。4.3 引擎选型的几个实际考量因素选引擎的时候除了看功能列表还有一些实际因素要考虑。团队的技术栈是第一个。如果你的团队都是C#背景硬上Unreal的C会很痛苦如果团队都是C老手用Unity的C#可能会觉得束手束脚。我见过一个团队程序全是C出身非要转Unity结果天天吐槽C#的性能和GC后来还是换回了UE。目标平台是第二个。手游优先选Unity主机和PC高画质优先选UE2D独立游戏可以看看Godot。这不是绝对的但大方向是这样。比如你想做Switch游戏Unity和UE都支持但Unity的打包流程更成熟一些。项目规模是第三个。小团队、短周期选Unity或者Godot快速出原型大团队、长周期、高画质选UE前期投入大但后期上限高。我个人的经验是如果项目预算低于50万别碰UE光是美术资源的制作成本就能把你拖垮。社区和资料是第四个。遇到问题能不能快速找到答案直接决定开发效率。Unity和UE的社区都很活跃但Unity的中文资料更多一些。Godot的英文资料为主中文社区还在成长。如果你英文阅读没问题Godot的文档质量其实很高。5. 常见问题与排查技巧实录5.1 引擎学习中的典型困惑刚开始学引擎的人最容易陷入两个极端要么觉得引擎太复杂什么都想学结果什么都学不精要么觉得引擎太简单拖拖拽拽就能做游戏结果遇到性能问题就懵了。我的建议是“先纵向再横向”。先选一个引擎把它的核心工作流跑通怎么导入资源、怎么搭场景、怎么写逻辑、怎么打包发布。这个过程走完你对引擎就有了整体认知。然后再横向扩展学渲染、学物理、学网络一个一个模块深入。还有一个常见困惑是“要不要学底层”。我的答案是看你的目标。如果你想做引擎程序员那图形学、物理、内存管理都得啃如果你想做游戏逻辑程序员那引擎的API和脚本层熟练就够了底层了解个大概就行。我见过太多人一上来就啃《Real-Time Rendering》啃了半年还在第三章项目一点没动。先做起来遇到问题再回去补理论效率高得多。5.2 资源导入与编码问题的排查“godot引擎游戏乱码”这个问题我专门查过。大部分情况下乱码的原因就三个字体不支持中文、文本编码不是UTF-8、或者导入设置里把文本当成了二进制。排查步骤很简单。第一步检查字体文件。Godot默认的字体不一定包含中文字形你需要导入一个支持中文的字体比如思源黑体或者文泉驿。导入之后在主题或者控件的字体设置里指定这个字体。第二步检查文本编码。Godot的脚本和文本资源默认用UTF-8如果你的文件是GBK或者其他编码就会乱码。用文本编辑器把文件转成UTF-8或者在导入设置里指定正确的编码。第三步检查导入类型。有些文本文件被误导入成了二进制资源引擎读出来就是乱码。在导入面板里把类型改成“Text”或者“CSV”重新导入。这个问题在Unity和Unreal里也会遇到本质是一样的字体、编码、导入设置。我踩过一次坑一个CSV配置文件用Excel保存成了GBKUnity读出来全是问号后来用Notepad转成UTF-8就好了。5.3 性能问题的快速定位方法引擎里的性能问题无非就是CPU瓶颈、GPU瓶颈、内存瓶颈。定位方法也不复杂关键是找对工具。CPU瓶颈用Profiler看。Unity有Profiler窗口Unreal有Unreal InsightsGodot有内置的Profiler。看哪个函数占用时间最多是逻辑更新、物理计算还是渲染提交。我遇到过一个项目帧率上不去Profiler一看是每帧都在做字符串拼接改成缓存之后帧率直接翻倍。GPU瓶颈用RenderDoc或者引擎自带的GPU Profiler看。看Draw Call数量、三角形数量、纹理带宽、着色器复杂度。Draw Call太多就做合批三角形太多就做LOD纹理带宽高就压缩纹理着色器复杂就简化计算。内存瓶颈用内存分析工具看。Unity有Memory ProfilerUnreal有Memory Insights。看哪些资源占了大头有没有重复加载有没有泄漏。我见过一个项目内存一直涨最后发现是事件监听没有取消注册对象一直被引用着GC回收不了。5.4 引擎升级与版本管理的坑引擎版本升级是个让人又爱又恨的事情。新版本有更好的功能、更少的Bug但升级过程可能引入新的问题。我经历过一次Unity大版本升级项目里一半的插件不兼容花了两个星期才全部替换。我的经验是小版本可以跟大版本要谨慎。小版本通常是修Bug和优化风险低大版本可能有API变更、渲染管线重构风险高。升级之前一定要备份最好在分支上做确认没问题再合并。还有一个坑是插件依赖。很多插件只支持特定版本的引擎升级引擎之前要先确认插件有没有更新。如果没有要么等插件作者更新要么找替代方案要么自己改插件源码。我现在的习惯是项目里尽量少用第三方插件能用引擎原生功能就用原生减少升级时的依赖。6. 引擎的未来走向与个人学习路径6.1 云游戏与引擎的适配挑战云游戏这个概念提了很多年最近几年随着网络基础设施的改善又开始热起来。云游戏对引擎的影响主要在两个方面输入延迟和渲染编码。输入延迟方面玩家的操作要传到云端服务器服务器算完再传回来这个往返时间如果超过50毫秒玩家就能感觉到“不跟手”。引擎需要做预测和回滚在服务器确认之前先本地模拟确认之后再校正。这对引擎的网络模块提出了很高的要求。渲染编码方面云端渲染完的画面要压缩成视频流传给玩家这个压缩过程会引入延迟和画质损失。引擎需要和编码器深度配合比如在渲染阶段就考虑编码的块划分减少压缩伪影。这方面UE和Unity都在做探索但还没有特别成熟的方案。我个人觉得云游戏短期内不会取代本地游戏但在某些场景下会有优势比如试玩、云VR、跨设备续玩。引擎对云游戏的支持会是一个长期演进的过程。6.2 AI辅助开发对引擎工作流的影响AI在游戏开发中的应用越来越广泛从美术资源生成到代码辅助都在改变传统的工作流。对引擎来说AI的影响主要体现在两个层面工具链和运行时。工具链层面AI可以帮助生成纹理、模型、动画减少美术的工作量。比如用AI生成PBR材质输入几张参考图输出法线、粗糙度、金属度贴图。这能大幅缩短美术制作周期但质量还需要人工把关。运行时层面AI可以用于NPC行为、动态难度调整、程序化生成。比如用强化学习训练NPC的战斗策略用生成模型动态生成关卡。这些技术目前还在实验阶段但潜力很大。对开发者来说AI不是替代而是增强。你需要学会怎么用AI工具提高效率同时保持对核心技术的理解。我现在的习惯是重复性的代码让AI写核心逻辑自己写AI生成的内容一定要审查不能直接拿来用。6.3 给不同阶段开发者的学习建议如果你是零基础想入行游戏开发我的建议是先选Unity跟着官方教程做一个完整的2D小游戏从场景搭建到打包发布全走一遍。这个过程会让你对引擎有直观的认识知道每个模块大概干什么。然后学C#基础学向量和矩阵的基本运算学物理和渲染的基本概念。不要一上来就啃理论先做出来再理解为什么。如果你有编程基础想转游戏引擎方向我的建议是选一个引擎深入同时补图形学和物理的基础。图形学推荐《Real-Time Rendering》和GAMES101物理推荐《Game Physics Engine Development》。然后自己动手写一个最小引擎把渲染、物理、资源管理都实现一遍。这个过程会很痛苦但收获也最大。如果你已经在做游戏开发想提升引擎层面的能力我的建议是深入你当前用的引擎读它的源码理解它的架构设计。Unity的C#层源码是开放的Unreal的C源码完全开放Godot的源码也在GitHub上。读源码的时候带着问题读比如“它是怎么管理资源的”“它是怎么调度渲染的”比泛泛地读效率高得多。最后分享一个我自己的习惯每学一个新模块就写一篇总结把原理、实操、踩坑都记下来。这个过程逼着你把知识梳理清楚也方便以后查阅。我现在的很多经验都是当年写总结的时候沉淀下来的。
返回列表