游戏引擎原理与实践 01:聊聊游戏引擎的前世今生

游戏引擎原理与实践 01:聊聊游戏引擎的前世今生

玩游戏的时候,我们总会惊叹于炸裂的特效、丝滑的动画、真实的物理碰撞。很多玩家会简单把优秀的游戏体验归功于游戏引擎,但到底什么是游戏引擎?哪怕混迹行业多年的开发者,想要用一两句话精准定义它,也不是一件容易的事。今天我们就来拆解游戏引擎,搞懂它是什么、从哪里来,以及引擎和游戏之间剪不断理还乱的关系。

Bilibili 同步视频

游戏引擎原理与实践 01:聊聊游戏引擎的前世今生

一、到底什么是游戏引擎?边界模糊的核心底座📦

网上的资料会给出这样一段标准化定义:

游戏引擎是一套预制好的、可编辑的游戏开发核心组件集合,它给开发者提供整套开发工具,目标是让大家不用从零造轮子,快速制作交互式游戏与实时图像应用,大多支持 Windows、MacOS、Linux 多平台。内部包含渲染模块、物理碰撞、音效、脚本、动画、AI、网络、场景管理等大大小小子系统。

通俗理解,引擎就像一堆提前造好的零部件,类似组装手机:CPU、屏幕、摄像头全部预制完毕,开发者只需要按照自己产品需求挑选组合,再填充游戏独有的美术、剧情、关卡逻辑即可。

但《游戏引擎架构》一书提出了一个很现实的观点:游戏和引擎的分界线,其实十分模糊 。

有的引擎渲染模块深度耦合业务逻辑,渲染代码直接知道如何绘制游戏里的妖兽角色;有的引擎只提供通用材质、着色器能力,怪物的外观、动作全部靠外部数据配置定义。不存在完美的切割方案,随着游戏设计不断迭代,引擎与游戏业务的边界还会不停发生变动。

我们可以用一段极简 C++ 伪代码感受两种耦合差异。

方案 1:引擎代码硬编码业务角色,引擎与游戏强耦合

cpp 复制代码
// 引擎渲染模块内部,直接写死游戏角色逻辑,耦合度极高
void RenderSystem::DrawOrc()
{
    // 硬编码妖兽的模型、贴图、动画参数
    Mesh orcMesh = LoadOrcMesh();
    Texture orcTex = LoadOrcTexture();
    PlayOrcAnimation(m_animComponent);
    m_renderDevice->DrawMesh(orcMesh, orcTex);
}

方案 2:通用渲染引擎,角色全部由外部数据驱动,低耦合

cpp 复制代码
// 通用渲染接口,引擎不关心画的是妖兽还是英雄
void RenderSystem::DrawGameObject(GameObjectData& data)
{
    // 模型、贴图、动画全部来自外部配置数据
    Mesh mesh = ResourceManager::LoadMesh(data.meshPath);
    Texture tex = ResourceManager::LoadTexture(data.texturePath);
    PlayAnimation(data.animClip);
    m_renderDevice->DrawMesh(mesh, tex);
}

代码说明:上面两段代码演示引擎耦合度区别。第一段把游戏业务逻辑写进引擎底层,换一个游戏项目就要大改引擎源码;第二种引擎只提供通用能力,游戏内容由配置数据描述,复用性更强。现实项目中,自研引擎往往偏向第一种,商业通用引擎偏向第二种。

我们换个怀旧的角度理解引擎的起源。把时钟拨回 80 年代 FC 红白机时代。

手里已经有《超级马里奥》完整代码,现在要开发《冒险岛》。两款游戏都是横版卷轴:角色在地图跳跃、踩踏怪物、卷轴滚动;区别只是美术素材、关卡、音效不一样。

没必要每做一个游戏全部从零手写。我们完全可以把角色运动、地图滚动、碰撞检测、输入响应这些通用逻辑抽离出来 ,只更换美术资源与关卡配置,就可以快速产出新游戏。这部分被抽离复用的代码,就是最原始形态的游戏引擎。
#mermaid-svg-jaA4nZXjeGcnyoo4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jaA4nZXjeGcnyoo4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jaA4nZXjeGcnyoo4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jaA4nZXjeGcnyoo4 .error-icon{fill:#552222;}#mermaid-svg-jaA4nZXjeGcnyoo4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jaA4nZXjeGcnyoo4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jaA4nZXjeGcnyoo4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jaA4nZXjeGcnyoo4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jaA4nZXjeGcnyoo4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jaA4nZXjeGcnyoo4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jaA4nZXjeGcnyoo4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jaA4nZXjeGcnyoo4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jaA4nZXjeGcnyoo4 .marker.cross{stroke:#333333;}#mermaid-svg-jaA4nZXjeGcnyoo4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jaA4nZXjeGcnyoo4 p{margin:0;}#mermaid-svg-jaA4nZXjeGcnyoo4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jaA4nZXjeGcnyoo4 .cluster-label text{fill:#333;}#mermaid-svg-jaA4nZXjeGcnyoo4 .cluster-label span{color:#333;}#mermaid-svg-jaA4nZXjeGcnyoo4 .cluster-label span p{background-color:transparent;}#mermaid-svg-jaA4nZXjeGcnyoo4 .label text,#mermaid-svg-jaA4nZXjeGcnyoo4 span{fill:#333;color:#333;}#mermaid-svg-jaA4nZXjeGcnyoo4 .node rect,#mermaid-svg-jaA4nZXjeGcnyoo4 .node circle,#mermaid-svg-jaA4nZXjeGcnyoo4 .node ellipse,#mermaid-svg-jaA4nZXjeGcnyoo4 .node polygon,#mermaid-svg-jaA4nZXjeGcnyoo4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jaA4nZXjeGcnyoo4 .rough-node .label text,#mermaid-svg-jaA4nZXjeGcnyoo4 .node .label text,#mermaid-svg-jaA4nZXjeGcnyoo4 .image-shape .label,#mermaid-svg-jaA4nZXjeGcnyoo4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-jaA4nZXjeGcnyoo4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jaA4nZXjeGcnyoo4 .rough-node .label,#mermaid-svg-jaA4nZXjeGcnyoo4 .node .label,#mermaid-svg-jaA4nZXjeGcnyoo4 .image-shape .label,#mermaid-svg-jaA4nZXjeGcnyoo4 .icon-shape .label{text-align:center;}#mermaid-svg-jaA4nZXjeGcnyoo4 .node.clickable{cursor:pointer;}#mermaid-svg-jaA4nZXjeGcnyoo4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jaA4nZXjeGcnyoo4 .arrowheadPath{fill:#333333;}#mermaid-svg-jaA4nZXjeGcnyoo4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jaA4nZXjeGcnyoo4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jaA4nZXjeGcnyoo4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jaA4nZXjeGcnyoo4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jaA4nZXjeGcnyoo4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jaA4nZXjeGcnyoo4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jaA4nZXjeGcnyoo4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jaA4nZXjeGcnyoo4 .cluster text{fill:#333;}#mermaid-svg-jaA4nZXjeGcnyoo4 .cluster span{color:#333;}#mermaid-svg-jaA4nZXjeGcnyoo4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jaA4nZXjeGcnyoo4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jaA4nZXjeGcnyoo4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-jaA4nZXjeGcnyoo4 .icon-shape,#mermaid-svg-jaA4nZXjeGcnyoo4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jaA4nZXjeGcnyoo4 .icon-shape p,#mermaid-svg-jaA4nZXjeGcnyoo4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jaA4nZXjeGcnyoo4 .icon-shape .label rect,#mermaid-svg-jaA4nZXjeGcnyoo4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jaA4nZXjeGcnyoo4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jaA4nZXjeGcnyoo4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jaA4nZXjeGcnyoo4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 超级马里奥完整项目代码
抽取通用逻辑:跳跃/碰撞/卷轴/输入
业务独有:马里奥美术、关卡、音效
原始横版游戏引擎
冒险岛美术关卡音效
产出冒险岛游戏

图 1:早期游戏引擎诞生逻辑示意图。从已有游戏项目剥离通用代码得到基础引擎,再填入全新游戏的特有资源,快速生成新游戏。

早期这种简陋的模板引擎有很大局限,只适配横版闯关。等到即时战略游戏兴起,需要多人联机、大批量同屏单位渲染、复杂 AI,旧模板完全扛不住。直到 id Software 卡马克带来《DOOM》、《QUAKE》,真正意义现代 3D 引擎登上舞台。《QUAKE》引擎对外开放,爱好者基于这套代码魔改,诞生了大名鼎鼎的 CS。这套引擎不光做射击游戏,不少其他品类项目也直接复用它的渲染、动画模块,大幅降低开发工作量,这也是现代游戏引擎发展史的关键转折点。

并不是纠结引擎的教科书定义有多么严谨。我们研究引擎的核心目的,是学会判断:什么引擎适合自己项目,不同引擎能力边界在哪里,帮助我们更好做游戏。

二、百花齐放到格局定型:自研引擎 vs 商业引擎⚖️

自从卡马克开启引擎商业化的苗头之后,行业分化出两条路线:团队从零打磨自研引擎,或者采购成熟商业引擎。

真正合格的商业引擎不只是一堆源码,它需要配套完善工具链、规范工作流,同时提供持续技术支持,项目踩坑的时候可以获得官方协助。但高质量商业引擎屈指可数,早年授权费用不菲。于是大量大厂选择自研引擎:育碧刺客信条引擎、EA 战地系列引擎、科乐美实况足球,每个重磅 IP 背后都有专属自研引擎持续迭代。一旦停止跟进技术潮流,引擎就会逐步被时代淘汰。

国内早期引擎生态中,Gamebryo 是应用广泛的商业引擎,Ogre 是热门开源非商业引擎。时过境迁,如今行业格局已经改变:移动端项目首选 Unity,PC 主机大量项目选用 Unreal 虚幻引擎。对比老一代引擎,二者最大优势就是低耦合、通用性强,可以支撑多种多样的游戏品类。

引擎名称 类型 时代定位
Gamebryo 商业引擎 国内早期商用主流
Ogre 开源引擎 早期学习、中小型项目
BigWorld 商业引擎 大型MMO网游常用
Unity 商业引擎 移动端为主,全平台通用
Unreal Engine 商业引擎 PC/主机,高画质项目
Cocos2d‑x 开源引擎 2D手游项目

表 1:几款经典游戏引擎简单对比表

那是不是意味着自研引擎已经走向没落?答案是否定。

  • ✅适合自研迭代:团队技术储备充足、开发周期宽裕,已有成熟游戏产品沉淀,持续迭代自家引擎可以最大化定制化、性能优势。

  • ✅适合选用商业引擎:项目工期紧张,新项目品类和旧引擎定位差异巨大,修改老引擎的人力成本超过学习商业引擎。

两种路线没有绝对好坏,只看项目的现实条件。

三、引擎一定要依附游戏而生吗?理想与现实的鸿沟🤔

有一个很经典的议题:开发游戏引擎,是否必须依托具体游戏项目迭代?

回顾历史,绝大多数传统引擎,都是伴随游戏产品打磨成长。游戏公司的首要目标是产出游戏,引擎作为工具服务游戏,游戏需要什么新特性,引擎就补什么功能。就算是虚幻引擎,早期也是依靠自家《虚幻竞技场》来验证能力。一款引擎如果没有经过商业游戏验证,普通开发者不敢轻易上手,底层出现问题很难自己修复。所以很多引擎仅限公司内部使用,出问题可以引擎组快速跟进。

海外有一部分团队尝试让引擎技术独立演进,引擎技术反过来推动游戏设计创新,而不是完全被游戏需求牵着鼻子走。但直到今天,"万能引擎" 依旧只是幻想,不存在引擎可以同时满足下面全部三点:

  1. 适配全部游戏类型,覆盖所有游戏功能;

  2. 轻松实现设计师天马行空的各类创意;

  3. 极致运行效率,业内事实:性能优化做到天花板的引擎,几乎都是针对特定游戏深度定制。

引擎团队心中的理想蓝图:底层与上层游戏业务维护互相分离,统一架构,一套引擎可以赋能多款完全不同品类游戏。但理想遇到现实,最大阻碍来自人的成本,包括技术人员能力、管理、招聘等一系列人力因素。虚幻 3 足够通用,但想要完全驾驭它,对开发者技术功底要求极高。

而 Unity 的出现打破了僵局。它不仅仅是简单做到组件化架构,更是巧妙解决 "人的成本" 这个难题。Unity 自身并不拥有现象级自研游戏,但是构建起庞大社区:世界各地开发者为它开发插件、工具。地形系统、IK 反向动力学、材质编辑器、技能系统、海量美术资源,大部分扩展能力来自社区贡献。官方只维护最核心底层,开发者社区源源不断补充上层能力,开发者之间互相受益。

Unity 巨大的竞争压力倒逼竞争对手进化。Unreal 选择开放源代码,吸引更多开发者做插件、沉淀社区方案,网上可以搜到绝大多数常见坑位的解决方案,社区生态愈发壮大。

写在最后💡

游戏引擎没有唯一标准答案,没有绝对完美。

从 FC 时代剥离出来的几百行通用逻辑,到如今几十万行代码的巨型商业引擎,引擎技术一路进化。自研引擎可以极致定制,商业引擎依靠社区生态降低门槛。做项目的时候,不必盲目追逐 "最强引擎",看清项目工期、团队能力、产品需求,选出适配自己的工具,才是最重要的。

本文参考:《游戏引擎原理与实践 卷 1 基础框架》

如果你需要,我可以继续为你扩展,补充更多引擎子系统(渲染、物理)的讲解,或是增加更多 C++ 示例代码。

相关推荐
bkspiderx1 小时前
Qt 的消息机制:事件驱动与信号槽的底层逻辑
开发语言·qt·信号槽·事件驱动·qt 的消息机制
晴天的雨.9921 小时前
【C++算法】三数之和
开发语言·c++·算法
pippocao1 小时前
王者荣耀日志组件BqLog 为何如此快之3——压缩日志执行路径优化
c++
AC赳赳老秦2 小时前
公开 CSV 数据集批量处理实战:用 OpenClaw 高效完成下载、清洗与标准化分析样本生成
java·开发语言·汇编·c++·python·deepseek·openclaw
j7~2 小时前
【Python】(篇七)《使用Python库》 -- 详解
开发语言·后端·python·编程学习·python标准库·python第三方库·python拓展
心中有你02142 小时前
C语言实现文件异或加密与RLE压缩算法
c语言·开发语言
凉茶钱2 小时前
【吃透C++】static、友元、内部类、匿名对象与拷贝编译器优化全解析
c++·青少年编程·visual studio
小宋10212 小时前
MCP 工具也有供应链风险:注册中心、签名校验、版本锁定与白名单
开发语言·网络·人工智能·php
无名猿2 小时前
左值引用、右值引用与万能引用:引用折叠全解
c++·现代c++·模板元编程·语法基础