《游戏引擎实现原理》子系统与工具 目录

《游戏引擎实现原理》卷三《子系统与工具》导航页:在卷一《引擎内核》与卷二《渲染引擎》之上,用 TypeScript 从零实现让《微光岛》活起来的资源管线与流式、动画与蒙皮、物理集成、音频、输入、脚本绑定与热重载,以及编辑器四章(数据模型、命令栈、属性面板、视口与资产)。讲清本卷定位、与卷一卷二的关系、L1–L4 可运行性口径,按三个部分列出 33 节索引,另含阅读路径、周计划与常见问题速查。

目录

欢迎来到《游戏引擎实现原理》卷三。卷一《引擎内核》里,我们从零写出了一个能跑的引擎内核 helio——它管内存、管句柄、管实体和组件、管并发。卷二《渲染引擎》里,我们给内核接上了一整套渲染能力,《微光岛》的小岛第一次被画到屏幕上,有光、有影、有后处理、有 UI。但到这一刻为止,这个世界还是静止的:资源要手动塞进去、角色不会动、撞不到东西、没有声音、按键没人听、改一行代码要重启整个游戏。

卷三《子系统与工具》要陪你做的,就是让《微光岛》真正活起来:用 TypeScript 从零实现一整套运行时子系统与工具链——资源管线与流式、动画接入与蒙皮、物理集成、音频、输入、脚本绑定与热重载,以及一个能用的编辑器。

为什么把这些放进同一卷?因为它们共享同一条底层规律:引擎要「认识」数据,才能驱动它、编辑它、热重载它。 资源管线的核心是「资源是什么、谁依赖谁、谁拥有谁」;反射驱动的属性暴露与编辑器属性面板,靠的是同一份类型描述符;命令栈、脏标记、文档模型,是编辑器与运行时之间那条「数据与代码分离」的接缝。把这些讲透,你拿到的不是七个互不相干的模块,而是一套「数据如何在引擎里流动」的统一视角。

市面上讲游戏引擎的书,大多把「子系统」当 API 手册来列。本卷走另一条路:每一节都写代码,每一节的代码都在本机真跑过,跑出来的是真实产物——真实字节数、真实耗时、真实像素、真实音频样本。 凡是给出「这样更好」的结论,背后都有一份能复现的测量。

这一卷写给谁

读者现状读完能拿到什么
读过卷一《引擎内核》的读者内核能跑,但没有内容、没有交互一套接在内核之上的子系统与工具,和「数据怎么被引擎驱动」的完整链条
读过卷二《渲染引擎》的读者画面能画,但世界是死的让渲染出来的世界动起来、碰得到、响得出、按得动、改得了
想做编辑器 / 工具链的开发者会用现成引擎的编辑器,不知道它怎么搭数据模型、命令栈、反射属性面板、视口与资产浏览器的从零实现
已懂子系统原理、想落地的读者懂「为什么要有音频总线 / 状态机」,缺「怎么写出来」一条从资源管线到编辑器的可复制实现路径,每节都能跑出产物

前置知识:读完卷一《引擎内核》,或至少了解句柄、ECS 与 Job System 的基本概念;会写 TypeScript。不需要安装任何游戏引擎——本卷全部代码在 Node 26 与 headless Chromium 下运行。

三类读者,三类收获

下面把这四类人分别展开,你可以对照自己的处境挑一段读——它决定了你该走哪条阅读路径。

如果你读过卷一、想把内核接出内容。 你手里已经有一个能算的 helio 内核,但它只会算、不会「用」。这一卷正好补上内容与交互那道接缝:内核提供世代句柄,资源层用它做资源引用与引用计数;内核提供固定步长循环,物理集成直接接在上面。读完你得到的是一条把「内容」喂进内核、再由内核驱动的完整通路。

如果你读过卷二、想让画面里的世界动起来。 你已经有了一台能画的渲染器,但小岛是静止的。这一卷给每个物体接上动画与蒙皮、给角色接上物理与输入、给场景接上音频——卷二渲染器一行都不用改,它拿到的仍是一批普通实例,只是这批实例的变换每帧都在变。

如果你想做编辑器 / 工具链。 编辑器四章(第 8–11 章)是本卷最独立的一块:场景文档模型、脏标记、可撤销命令栈、反射驱动的属性面板、视口与资产浏览器。它不是「某个引擎的编辑器怎么用」,而是「编辑器作者怎么从零搭出一个能编辑、能撤销、能存盘的最小编辑器」。

如果你已懂子系统原理、只缺落地。 你懂「为什么要有音频总线」「为什么状态机要配混合树」,缺的是「怎么写出一个可跑的」。这一卷的 33 节本身就是一份施工顺序表:先立资源管线,再补动画、物理、音频、输入、脚本,最后装配编辑器。每一节都能独立跑通,你可以在任何一步停下,把它接到自己的项目里。

与卷一《引擎内核》、卷二《渲染引擎》的关系

三卷不是「上中下三本」,而是**「地基 / 楼 / 生活」**。卷三里的每一行代码,几乎都能在卷一卷二里找到它依赖的那根桩:

卷三要建的它接在卷一卷二的哪一处
资源对象与生命周期复用卷一第 6 章的世代句柄做资源引用
资源管线与流式消费卷二第 6 章的纹理 / 网格产物与第 4.2 章的上传路径
动画与蒙皮复用卷二第 7 章的变换层级与第 6.1 章的顶点格式
物理集成复用卷一第 15 章的固定步长循环与任务图
音频混音复用卷一第 4 章的缓冲布局;DSP 数学不依赖前两卷
输入与动作映射复用卷一第 16 章的类型描述符做数据化绑定
脚本绑定与热重载复用卷一第 3 章的拓扑排序与第 16 章的反射
编辑器四章复用卷一第 6 章句柄与第 16 章反射、卷二第 7 章场景图与第 11 章视口

内核、渲染与子系统的接缝只有一句话:内核产出「数据 + 句柄」,渲染把数据变成像素,子系统让数据动起来、并把数据暴露给工具。 这条缝越清晰,三层越能各自演进——所以卷三往 src/subsys/ 与 src/editor/ 里加代码时,卷一卷二一行都不用改。

还没读前两卷的读者,建议先去卷一目录 与卷二目录 过一遍;至少读完卷一第 4 章(内存布局)、第 6 章(句柄)、第 16 章(反射)与卷二第 6–7 章(几何与场景),再回来看卷三会轻松很多。

本卷的差异化

和「讲子系统原理」或「讲某个引擎编辑器」的书比,本卷有三条很硬的不同:

  • 从零实现,不调框架。 我们不接现成的物理库、音频框架或编辑器 UI 框架,而是自己定义资源模型、混音图、命令栈、面板描述模型这一整套抽象,再为它写真实的实现(含一个真的 BC1 编码器、一条真的拓扑序混音链)。你看到的是「引擎作者怎么写子系统」,不是「怎么用某个库」。
  • 每节可跑出真实产物。 每一节都产出一份能在本机运行、能读到数字的东西:一份逐帧的流式报告、一份蒙皮基准的计时、一段与浏览器逐样本一致的音频、一张回读像素核对过的视口。结论不是「理论上会更快」,而是「本机实测快了这么多」。
  • 真实字节、耗时与样本。 凡是给出「这样更好」的结论,背后都有一份真实测量。看到数字时,请连着它的测量条件一起看,而不是只记那个倍数。

本卷的可运行性口径

本套书的每一节都标注它属于哪一层,你据此知道「这一节的东西能不能在你机器上跑起来」:

层级含义你怎么验证
L1纯 TypeScript 逻辑,零运行时依赖Node 26 直接跑 .ts,或 vitest 跑单测
L2需要 GPU / 浏览器用 headless Chromium 跑(音频、视口、蒙皮 demo)
L3只能做类型检查tsc --noEmit 通过
L4命令行不可复现正文会明确标注「需手动验证」
B性能基准跑出计时 JSON,数字写进正文对照表

这五层各自意味着什么,值得展开说清楚:

  • L1——能真跑。 资源依赖图、引用计数、关键帧求值、状态机转移、混音求和、输入缓冲、命令栈、文档模型与脏标记,这些都是纯 TypeScript,node src/xxx.ts 就能跑,配 vitest 就能断言。你看到的每一段 L1 代码,都是在本机跑过、被单测盯过的。
  • L2——需要 GPU / 浏览器。 凡是碰 GPU 或 Web Audio 的代码——GPU 蒙皮、视口渲染、OfflineAudioContext 复算——都得靠 headless Chromium 跑起来验证。本卷会给出统一的跑法,让你在本机也能复现每一张图和每一段音频。
  • L3——只能类型检查。 编辑器里的 DOM 胶水与控件创建(属性面板、控件映射)依赖只在浏览器里存在的类型,能通过 tsc --noEmit 但无法直接执行。这类代码会被明确标注,正文会说明「为什么它跑不了」。
  • L4——命令行不可复现。 涉及真实硬件或真实交互的观察(真实音频设备、真机输入、鼠标拖拽 gizmo、真实 HMR 刷新时序)命令行里复现不了。正文会标「需手动验证」,并给出你该观察什么。
  • B——性能基准。 凡是给出「快了 N 倍」的结论,背后都有一份 B 级基准:它跑出计时 JSON,原始数字写进对照表。看到 B 级结论时,请连着原始数据一起看。

卷三编辑器 4 章是本卷 L3 / L4 的重灾区:数据模型(第 8 章)与命令栈(第 9 章)是 L1,必须真跑并有 vitest 用例;属性面板(第 10 章)与视口 / 资产(第 11 章)是 L3 / L4,正文会显式标注「需在浏览器中手动验证」,但其中的反射驱动逻辑与拾取几何仍被抽成 L1 部分真跑(10.1 的面板描述模型、11.1 的拾取射线都有 vitest 用例)。

贯穿项目:《微光岛》从静止走进生活

卷一里那个「会走动的方块」,在卷二里被真正画了出来;这一卷里,它要活。全书仍然只有这一个项目:《微光岛》——一款低多边形 3D 收集冒险小游戏。卷三要做的,是给这个小岛接上一整套子系统与工具:

需求牵出的技术点对应章节
美术资源进得来、存得下资源模型、导入器、离线产物1、2
世界很大、内存装不下异步加载、缓存、增量构建、流式区块1、2
角色会走路、会挥剑剪辑、轨道、求值器、状态机、混合树3
角色真的「有骨头」骨骼、蒙皮矩阵、GPU 蒙皮、性能基准3
角色撞得到墙、站得稳物理边界、固定步长、插值、穿透4
岛上能听见风声、脚步、BGM混音图、音频总线、音效池、流式5
键鼠、手柄、触摸都能玩设备归一化、动作映射、输入缓冲6
脚本能读写组件、能热重载反射属性、生命周期、模块替换与状态迁移7
关卡能编辑、能撤销、能存盘文档模型、命令栈、场景格式8、9
属性面板、视口、资产浏览器反射面板、控件映射、拾取、视口渲染10、11

范围红线:本卷做到「《微光岛》在真实运行时的子系统驱动下活起来,并能在编辑器里被编辑、撤销与存盘」为止。网络同步、服务端权威、跨平台打包等更上层的内容留给后续卷次。

关于基准数字的诚实声明

本卷会出现不少性能数字——加载耗时、驻留字节、蒙皮倍数、调色板开销。它们都来自作者本机实测,并附带测量条件。但它们有一个必须说清楚的边界:

这些数字是同机相对量级,绝对数值不可移植。 Node 版本、CPU 调频、后台负载都会影响结果;同一段代码,换个并发上限、换个区块粒度,耗时可能差出一截。所以当你看到「并发 8 时加载耗时降到 94.6 ms」时,请把重点放在方向而不是倍数上。

真正稳定的结论是那些量级差异,比如「并发从 1 到 8,加载 64 个资源的墙钟耗时几乎线性下降」——这种结论不会因为环境微调而翻转。本卷会刻意把「方向性结论」和「具体倍数」分开表述:前者你可以直接采信,后者请当作「作者机器上的参考值」,并鼓励你在自己的机器上复现一遍。

三条阅读路径

本卷 11 章 33 节,一次读完大约 4 周(每天一小时),但不是每个人都该按同一种顺序读。下面三条路径覆盖了绝大多数人的需求。

路径 A:系统通读(约 4 周)

最完整的一条。按顺序走完 11 章,每章的代码都亲手敲一遍。

第 1–2 章(资源与流式)→ 第 3–5 章(动画、物理、音频)
→ 第 6–7 章(输入、脚本绑定与热重载)→ 第 8–11 章(编辑器)

适合谁:想把子系统与工具链从头搭到尾、或打算自研引擎 / 编辑器的读者。第 11 章会把前面所有零件装配成一个能编辑、能撤销、能存盘的最小编辑器,不要跳过。这条路径最慢,但拿到的是一套完整的、能自己复现的运行时与工具。

路径 B:只读运行时子系统(第 3–7 章,约 2 周)

已经有资源管线、只想让世界「动起来」的话:

3.1 → 3.2 → 3.3 → 4.1 → 4.2 → 4.3 → 5.1 → 5.2 → 5.3 → 6.1 → 6.2 → 6.3 → 7.1 → 7.2 → 7.3

适合谁:时间有限、目标是「让游戏跑起来」的读者。这 15 节从动画一路走到脚本热重载,是一条自洽的线。建议至少把第 3 章(动画与蒙皮)和第 4 章(物理)完整读——它们决定了角色「看起来对不对」和「撞起来稳不稳」。

路径 C:直奔编辑器(第 8–11 章,约 1–2 周)

只想知道「一个编辑器怎么从零搭出来」的话:

8.1 → 8.2 → 8.3 → 9.1 → 9.2 → 9.3 → 10.1 → 10.2 → 10.3 → 11.1 → 11.2 → 11.3

适合谁:已经有一个能跑的游戏、想给它配一个编辑器的读者。这条路径最独立,但会牺牲「资源与运行时怎么把数据准备成可编辑的形状」。建议把 8.1 编辑器与运行时的关系 当作前置先读,它给了整条「编辑器与运行时共享什么、隔离什么」的全貌。

路径 D:按问题查(随时)

不打算通读,只在遇到具体问题时翻对应章节。

加载慢 / 进度条不动 → 1.2;内存泄漏 → 1.3;改一个资源要重导全部 → 2.2
世界太大装不下 → 2.3;动画过渡生硬 → 3.2;角色动起来掉帧 → 3.3
物理抖动 / 穿透 → 4.2;第一次没声音 → 5.3;跳跃被「吃掉」 → 6.3
脚本回调顺序乱 → 7.2;编辑器 Play 后编辑态被污染 → 8.1;没有 Ctrl+Z → 9.1
每个组件都要手写面板 → 10.1;鼠标点不中物体 → 11.1

适合谁:已经有一个能跑的游戏、正被某个具体问题卡住的读者。下节「常见问题速查」就是为这条路准备的——它把「你想解决的问题」直接映射到「去看哪一节」。

33 节完整索引

下面把 33 节按三个部分分组,每节链到它的正文。想跳读时,从这里直接进。

第一部分:资源与流式(第 1–2 章)

让资源「进得来、存得下、装得对」——从资源模型与依赖图起步,经异步加载、引用计数、离线导入、增量构建,最后落到运行时的流式区块。

节文章核心内容
1.11.1 资源模型、GUID 与依赖图GUID、类型与路径;Kahn 拓扑加载序 + DFS 三色循环检测
1.21.2 异步加载与进度并发上限、优先级队列、in-flight 去重、字节进度回调
1.31.3 引用计数、生命周期与释放引用计数表、延迟释放队列、帧末 collect 统一释放
2.12.1 资源导入器与离线处理注册表分派、版本失效、PPM / BC1 / mip 链、自描述产物
2.22.2 缓存、热重载与增量构建FNV-1a 内容哈希、依赖键沿 DAG 传播、增量重导脏子树
2.32.3 流式加载与区块(《微光岛》的岛)4x4 区块、距离优先级、每帧字节预算、双半径滞回

第二部分:运行时子系统(第 3–7 章)

让《微光岛》「动起来、碰得到、响得出、按得动、改得了」——动画与蒙皮、物理集成、音频、输入、脚本绑定与热重载。

节文章核心内容
3.13.1 剪辑、轨道与求值器时间轴钳制 / 循环 / 乒乓、关键帧二分、step / linear / cubic 与 slerp
3.23.2 状态机与混合树参数驱动转移、exitTime、any-state、交叉淡化与一维混合树
3.33.3 蒙皮集成与性能基准CPU LBS + WGSL 蒙皮、CPU / GPU 逐顶点比对、蒙皮代价基准
4.14.1 引擎与物理库的边界判别联合碰撞体、PhysicsBackend 接口、ColliderSync 版本同步
4.24.2 固定步长、插值与穿透固定步长累加器、上一态与当前态插值、扫掠 AABB 防穿透
4.34.3 空间查询、分层与事件位掩码分层、raycast 与 overlap、enter / exit / stay 三态事件
5.15.1 混音图与音频总线有向图、拓扑序求和、OfflineAudioContext 逐样本比对
5.25.2 音效池、BGM 与流式voice 池、优先级抢占、淡入淡出包络、流式重采样与欠载检测
5.35.3 Web Audio 的限制与解锁自动播放解锁、渲染量子量化、延迟补偿、设备缓冲
6.16.1 设备抽象与事件队列四种设备归一化 InputEvent、帧内队列、边沿 / 电平语义
6.26.2 动作映射与重绑定动作→控制绑定表、多设备并行、冲突检测、版本化持久化
6.36.3 输入缓冲与手感跳跃缓冲、土狼时间、输入延迟补偿(手感数值属 L4)
7.17.1 反射驱动的属性暴露类型描述符→属性视图、类型强制、只读与隐藏、最小变更集
7.27.2 组件脚本的生命周期onCreate / onUpdate / onDestroy、状态机、帧末延迟卸载
7.37.3 模块替换与状态迁移数据与代码分离、版本戳、迁移链升版(真实刷新时序属 L4)

第三部分:编辑器(第 8–11 章)

让内容「做得出来、改得回去」——场景文档模型、可撤销命令栈、反射驱动的属性面板、视口与资产浏览器。

节文章核心内容
8.18.1 编辑器与运行时的关系共享文档、编辑态 / 运行态两实例、Play / Pause / Stop 与快照
8.28.2 文档模型与脏标记扁平节点表、id 与稳定 uid、脏标记沿父链传播、变更记录
8.38.3 序列化落盘与场景格式JSON 关卡格式、uid 引用、版本字段与迁移、拒绝更高版本
9.19.1 命令模式与可撤销操作Command 的 do / undo、撤销栈、redo 链断裂、合并判定
9.29.2 事务、合并与撤销栈原子事务补偿回滚、时间窗合并、栈条数上限与字节预算
9.39.3 与数据模型的接缝文档为唯一编辑目标、SetProperty / AddNode、命令→脏标记→重绘
10.110.1 反射驱动的属性面板面板描述数据模型(纯数据,L1);DOM 渲染属 L3
10.210.2 编辑器控件与类型映射类型→控件映射表、可覆写注册表(映射 L1);控件创建属 L3
10.310.3 多选编辑与批量修改公共属性、全等 / 混合判定、批量命令(L1);面板交互属 L3 / L4
11.111.1 gizmo 与拾取射线像素→NDC→世界射线、轴 / 面手柄、沿轴拖拽求解(拖拽属 L4)
11.211.2 场景视口与渲染接入渲染器接进视口、双相机、片元描边、网格叠加(回读像素核对)
11.311.3 资产浏览器与关卡编辑资产树、类型判定、稳定 GUID、拖放载荷、关卡加载(拖放属 L4)

周计划表

按路径 A 的顺序通读,可以照下面这张表安排节奏(每天约一小时):

周次章节本周焦点本周产物
第 1 周第 1–2 章资源模型、依赖图、异步加载、导入与增量、流式区块一条能拓扑加载、能流式装岛的管线
第 2 周第 3–5 章动画与蒙皮、物理集成、音频一个会动、会碰、会响的角色
第 3 周第 6–7 章输入与动作映射、脚本绑定与热重载一套可重绑定的输入与可热替换的脚本
第 4 周第 8–11 章编辑器四章一个能编辑、能撤销、能存盘的最小编辑器

如果只有零散时间,可以只挑第 1–2 章(资源与流式)或第 8–11 章(编辑器)这两段——它们各自成篇,是整卷最容易被单独拿走的两块。

常见问题速查

你想解决的问题去看
资源加载顺序不对、循环依赖报错1.1
加载慢、进度条不动或停在 0.971.2
资源删了还在用 / 内存泄漏1.3
美术资源进不来、格式不统一2.1
改一个资源要重导全部2.2
世界太大内存装不下、区块抖动2.3
动画时间轴不对、循环跳变3.1
两段动画过渡生硬、状态切错3.2
角色动起来掉帧3.3
不知道物理库该怎么接进引擎4.1
物理抖动、高速物体穿透4.2
射线打不中 / 接触事件重复触发4.3
音效求和爆音、总线增益不对5.1
同时响太多音效卡顿5.2
第一次没有声音5.3
每种设备各写一遍输入分支6.1
想让玩家自定义按键6.2
跳跃输入被「吃掉」6.3
编辑器要读写组件字段7.1
脚本回调顺序乱、报错就崩7.2
改脚本要重启整个游戏7.3
编辑器 Play 后编辑态被污染8.1
改一个节点不知道要重算谁8.2
关卡存盘后打不开 / 版本冲突8.3
编辑器没有 Ctrl+Z9.1
一次多选要撤销十几步9.2
命令改了数据但界面不刷新9.3
每个组件都要手写属性面板10.1
字段该配什么控件10.2
多选编辑显示不对10.3
鼠标点不中物体、拖拽漂移11.1
视口不显示 / 描边不对11.2
资产浏览器不会建树、关卡装不进来11.3

怎么用这份索引

  • 想系统学:走路径 A,从第 1 章 1.1 开始。
  • 想让世界动起来:走路径 B,从第 3 章 3.1 开始,把运行时子系统 15 节吃透。
  • 想搭编辑器:走路径 C,从第 8 章 8.1 开始。
  • 想查问题:直接用上面的「常见问题速查」,或跳到对应小节。
  • 想配代码:每一节的代码块都标了层级与来源文件,L1 的可以直接复制出来在 Node 26 上跑,L2 的按正文给的 headless Chromium 跑法复现。
  • 每篇正文末行都有「上一节 / 下一节」导航,也可以随时回到本页或卷索引 。

相关阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「engine」更多文章

  1. 《游戏引擎实现原理》工程与实战 目录
  2. 《游戏引擎实现原理》9.3 选型与自研的判断标准
  3. 《游戏引擎实现原理》9.2 同一个特性在不同引擎里的实现路径