23.现代游戏引擎入门总结
23.1 核心要点速览
引擎基础
游戏引擎本质认知
游戏 = 实时虚拟世界操作系统
- 游戏用 0/1 模拟图形、物理、动画、音频、网络等,本质是对真实世界的近实时模拟
- 实时性是第一约束:算法必须在 16.6ms(60FPS)或 33ms(30FPS)帧预算内完成,否则不具备引擎价值
- 引擎是”框架 + 运行时 + 工具链”的集合,渲染仅是模块之一,难点是多系统在帧预算内稳定协同
引擎 ≠ 渲染引擎
- 《Game Engine Architecture》架构图显示,Rendering 在引擎中占比极小
- 真正难点:将渲染、物理、动画、AI、网络、内存管理等几十个子系统在毫秒级预算内拼装运行
- 引擎是”复杂性的艺术”——在算力、带宽、内存硬约束下做取舍
引擎核心特质与制作难点
| 特质 | 说明 |
|---|---|
| 虚拟世界底座 | 用 0/1 逻辑模拟物理、光影等底层规则,支撑从 2D 像素到次世代开放世界 |
| 创意生产力工具 | 面向美术、设计师、工程师,提供可视化编辑器与高扩展性 API |
| 约束下的复杂系统 | 在”画质体验”与”硬件限制”间平衡,无完美架构 |
制作难点:
- 硬件边界:算力、内存、IO/带宽上限
- 网络挑战:延迟、抖动带来的同步难题
- Real-Time 约束:算法再好看,超帧预算就不可用
- 工具链与协作:无代码创作体验 + 工程可扩展性
- 可演进架构:新特性不破坏既有内容与工作流
游戏引擎发展脉络
| 阶段 | 特点 |
|---|---|
| 早期(无引擎) | 针对特定硬件从零编写,程序与内容高度耦合,无复用性 |
| DOOM 时代 | 卡马克首次分离可复用核心功能,形成”DOOM引擎”并商业化授权 |
| Quake 时代 | 实时 3D 渲染突破,GPU 诞生,引擎与硬件深度绑定 |
| 现代生态 | 商业引擎(UE/Unity)、自研引擎(寒霜/Anvil)、开源引擎(Godot)、专业中间件(PhysX/Wwise)多元共存 |
学习路径与方法
核心原则:先搭”骨架”再看”器官”——从分层架构、主循环/调度、数据组织建立系统认知,再深入各模块。
推荐方法:
- 从 Tick 解剖引擎:沿
Update/Tick追踪游戏循环 → 调度 → 各系统时序 - 搭架构地图:用《Game Engine Architecture》建立分层/模块/数据流模型
- 边学边做小引擎:基于 Piccolo 等迷你引擎练手改造
- 模块化精进:按”渲染 → 动画 → 物理 → 资源/脚本/玩法 → 工具链”逐块攻克
- 对照法理解前沿:对比 UE5/Unity 文档,验证 Lumen/Nanite 等设计取舍
引擎分层架构
“5+1”纵向分层
| 层级 | 职责 | 特点 |
|---|---|---|
| 工具层 | 编辑器、关卡编辑器、蓝图等可视化工具 | 非实时,面向开发者 |
| 功能层 | 渲染、动画、物理、AI 等运行时功能 | Tick 驱动,高度迭代 |
| 资源层 | 资产导入、GUID 管理、生命周期调度 | 格式统一、引用稳定 |
| 核心层 | 数学库、容器、内存管理 | 高性能基建,稳定少变 |
| 平台层 | RHI 图形接口、硬件抽象 | 屏蔽平台差异 |
| 中间件 | PhysX、Wwise、SpeedTree 等 | 贯穿各层的专业库 |
设计原则:上层依赖下层,禁止反向耦合;下层稳定、上层灵活。
资源层核心机制
资产化流程:原始资源(.max/.psd/.fbx)→ 引擎专属格式(DDS 等)→ 复合资产打包
| 机制 | 作用 |
|---|---|
| GUID | 全局唯一标识符,资产”身份证号”,脱离路径依赖,支撑网状关联 |
| Handle | 句柄系统,上层不直接持有内存地址,资产卸载重载后仍可安全访问 |
| 延迟加载 | 玩家”走到哪加载哪”,优先低精度再补高清 |
| 垃圾回收 | 分帧回收避免卡顿尖峰 |
功能层:Tick 驱动与多线程
Tick = 世界的最小时间片
- 典型流程:
Tick Logic → Tick Render,逻辑先于渲染 - 固定步长 vs 可变步长:逻辑常用固定步长(如 60Hz)保证物理稳定;渲染跟随显示刷新
- 推荐方案:固定逻辑步长 + 插值渲染(Gaffer “Fix Your Timestep”)
多线程架构
| 线程 | 职责 |
|---|---|
| 游戏/逻辑线程 | 玩法逻辑、AI、动画状态机 |
| 渲染线程 | 提交渲染命令、与 RHI 并行 |
| IO 线程 | 后台加载、解压、反序列化 |
Job System:将可并行工作(蒙皮、裁剪、粒子更新)拆成原子任务,按依赖关系分发到多核执行。
核心层:高性能基建
数学库特点
- 偏向”近似但快”而非”高精度但慢”
- 利用 SIMD/SSE/AVX 批量计算(一条指令算一排数据)
- 核心难点:矩阵排布、Cache 友好、四元数存储优化
引擎容器 vs STL
| STL 缺点 | 引擎容器优化 |
|---|---|
| 扩容策略不可控 | 可控增长、固定容量 |
| 易产生内存碎片 | 连续内存、少碎片 |
| 访问顺序不可控 | 鼓励顺序遍历,提高 Cache 命中率 |
内存管理三金律:数据放一起、顺序访问、批量读写
平台层与 RHI
RHI(Render Hardware Interface):统一封装不同图形 API(DirectX/OpenGL/Vulkan/Metal),上层用统一接口编程,平台层负责映射到具体实现。
游戏对象组件化
万物皆 GameObject,能力由组件提供
- 传统继承:耦合紧、共同父类难找、扩展难(水陆两栖坦克继承坦克还是船?)
- 组件化:对象作为”组件容器”,通过挂载/卸载组件灵活调整能力
| 对象类型 | Transform | Model | Motor | AI | Combat |
|---|---|---|---|---|---|
| 普通无人机 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 武装无人机 | ✅ | ✅ | ✅ | ✅ | ✅ |
组件化优势:解耦与灵活,新增功能只需添加组件,删除功能只需移除组件。
Tick 驱动模式对比
| 模式 | 逻辑 | 优点 | 缺点 |
|---|---|---|---|
| 对象化 Tick | 按对象顺序更新内部组件 | 直观易调试 | 无法并行、Cache 命中率低 |
| 组件化 Tick | 按组件类型批量更新 | 可并行、Cache 友好 | 逻辑分散、调试成本略高 |
现代引擎选择:组件化 Tick(并行高效),同时提供对象化 Tick 兼容接口。
事件机制与场景管理
事件机制三角色:
| 角色 | 职责 |
|---|---|
| 发送者 | 构造并发布事件,不关心谁响应 |
| 事件 | 消息载体,包含交互参数 |
| 接收者 | 订阅事件,触发时执行自身逻辑 |
核心思想:发送者只负责”通知”,接收者自己负责”响应”。
场景管理核心职责:对象生命周期管理、高效对象查询、层级关系维护
空间分割策略:解决”大海捞针”问题,避免 O(n²) 遍历
时序一致性
问题根源:多核并行 Tick、直接事件通信、组件循环依赖
解决方案:
| 方案 | 作用 |
|---|---|
| 邮局模式 | 事件先存队列,隔帧统一分发 |
| Pre/Post Tick | 拆分 Tick 为预准备→主逻辑→后同步 |
| 父子对象规则 | 父先 Tick、子后 Tick |
核心目标:保证游戏确定性(Deterministic),支撑精彩回放、多人一致性。
渲染系统
渲染概述
游戏渲染 vs 计算机图形学
| 对比维度 | 计算机图形学 | 游戏渲染 |
|---|---|---|
| 目标 | 效果能否实现 | 效果能否在 33ms 内实现 |
| 约束 | 无实时要求 | 严格帧预算,与逻辑/物理/网络共享资源 |
| 保真度 | 物理绝对正确 | 视觉感知正确即可 |
核心挑战:复杂度(数万对象)、硬件架构(CPU+GPU 异构)、性能与画质、资源竞争(渲染仅占 10%-20% CPU)
渲染管线数据流
顶点数据 → 顶点变换 → 图元组装 → 光栅化 → 片段着色 → 像素输出
- 投影:透视投影(模拟人眼)/ 正交投影(CAD/2D 界面)
- 光栅化:确定三角形覆盖哪些像素,生成片段
- 着色:着色器执行 ALU 计算 + 纹理采样 + 分支判断
GPU 架构与优化
SIMD vs SIMT
| 模型 | 说明 |
|---|---|
| SIMD | 单指令多数据,一条指令对多个数据执行相同操作 |
| SIMT | 单指令多线程,GPU 采用,32 线程一组(Warp)并行执行 |
CPU-GPU 数据流优化原则
- 最小化数据传输:避免 CPU-GPU 来回拷贝
- 警惕读回操作:从 GPU 读回数据会造成流水线停滞
- 利用缓存:让数据访问具有空间局部性
可渲染对象数据构成
| 数据类型 | 说明 |
|---|---|
| 网格(Mesh) | 顶点缓冲区 + 索引缓冲区,定义几何形状 |
| 材质(Material) | 定义光学属性,引用 Shader 和纹理 |
| 变换(Transform) | 位置、旋转、缩放 |
SubMesh:复杂模型按材质划分为多个子网格,每个 SubMesh 关联独立材质,通过多次 Draw Call 渲染。
资源池化:渲染资源创建资源池,场景中的对象是资源的实例化,避免重复存储。
渲染方程与光照模型
渲染方程:看到的亮度 = 自发光 + 来自所有方向的入射光经表面反射后进入眼睛的光的总和
三大挑战:光照亮哪些区域(阴影)、半球面积分计算成本高、入射光本身需要递归计算
简化光源类型
| 类型 | 模拟对象 | 特点 |
|---|---|---|
| 方向光 | 太阳 | 光线平行,强度不衰减 |
| 点光源 | 灯泡 | 向所有方向照射,强度随距离衰减 |
| 聚光灯 | 手电筒 | 锥形照射,有方向和范围 |
| 环境光 | 间接光照 | 无方向,均匀基础亮度 |
Blinn-Phong 模型:环境光 + 漫反射 + 高光反射,简单高效但能量不守恒
Shadow Map:从光源渲染深度图,比较像素深度判断是否在阴影中;问题:自遮挡、精度走样
全局光照
直接光照 + 间接光照 = 全局光照
预计算全局光照方法
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| LightMap | 静态场景 | 运行时高效,精细细节 | 离线烘焙慢,仅静态 |
| Light Probe | 动态物体 | 可插值,支持动态 | 采样率不足,细节有限 |
| Reflection Probe | 镜面反射 | 高质量反射 | 存储成本高 |
球谐函数(SH):将球面光照信号压缩为少量参数,12 个系数即可描述整个球面光照分布
PBR 材质模型
微表面理论:表面由大量方向各异的微平面组成,法线分布决定材质视觉特性
DFG 三项
| 项 | 名称 | 作用 |
|---|---|---|
| D | 法线分布函数 | 决定高光形状和大小,受粗糙度影响 |
| F | 菲涅尔项 | 掠射角反射更强,受金属度影响 |
| G | 几何衰减项 | 微表面自遮挡和自阴影 |
MR vs SG 工作流
| 工作流 | 纹理 | 优点 | 缺点 |
|---|---|---|---|
| Metallic-Roughness | BaseColor + Roughness + Metallic | 简单、省内存、不易出错 | 无法直接控制电介质 F0 |
| Specular-Glossiness | Diffuse + Specular + Glossiness | 精确控制 F0、边缘质量好 | 易出错、内存占用大 |
IBL 与阴影技术
基于图像的光照(IBL):用环境贴图表示来自各方向的远距离光照,预计算积分项
| 组件 | 说明 |
|---|---|
| 漫反射辐照度图 | 对环境贴图模糊处理,存储每个方向的辐照度值 |
| 预滤波环境贴图 | 按粗糙度预计算不同模糊程度的高光反射 |
| BRDF 积分 LUT | 存储 BRDF 的积分结果,运行时查表 |
阴影技术
| 技术 | 说明 |
|---|---|
| 级联阴影(CSM) | 将视锥体分割为多个子视锥体,解决大世界阴影精度问题 |
| PCF | 百分比渐近滤波,多次采样阴影贴图并平均,平滑阴影边缘 |
| PCSS | 基于物理的软阴影,根据光源大小和遮挡关系计算真实半影宽度 |
| VSSM | 方差软阴影贴图,利用深度均值和方差近似遮挡程度,效率高 |
地形、大气与云渲染
地形渲染
| 技术 | 说明 |
|---|---|
| 高度图 | 俯视图记录高度值,简单但无法表示洞穴/悬垂结构 |
| 四叉树细分 | 递归划分区域,退化三角形处理 T-junction |
| GPU 曲面细分 | Hull Shader → Tessellator → Domain Shader |
| 体素化地形 | 用体素表示 3D 空间,支持洞穴/悬垂结构 |
大气散射
| 散射类型 | 特点 |
|---|---|
| 瑞利散射 | 粒子远小于波长,散射强度与波长四次方成反比,天空呈蓝色 |
| 米氏散射 | 粒子接近或大于波长,前向散射强,产生光晕和雾效 |
预计算 LUT:透射率 LUT、单次散射 LUT、多重散射 LUT,避免实时计算复杂积分
体积云:天气纹理 + Perlin/Worley 噪声生成云密度模型,光线步进渲染
后处理
| 技术 | 说明 |
|---|---|
| AO | 预计算AO/SSAO/HBAO/GTAO/RTX AO,模拟环境光遮蔽 |
| 抗锯齿 | SSAA(质量最好开销最大)/MSAA(平衡)/FXAA(快但一般)/TAA(时序) |
| Bloom | 提取高亮区域 → 高斯模糊 → 与原图混合,模拟光晕效果 |
| Tone Mapping | HDR 到 SDR 的映射,ACES 曲线是 3A 游戏主流选择 |
| Color Grading | 通过 LUT 调整画面色调,烘托氛围,最具成本效益的后处理 |
渲染管线架构
| 管线 | 核心思想 | 优点 | 缺点 |
|---|---|---|---|
| Forward | 按物体遍历渲染 | 简单、支持 MSAA | 多光源开销大(物体×光源) |
| Deferred | 先渲染 G-Buffer,再逐像素着色 | 高效处理多光源 | 高内存带宽、不支持 MSAA、不支持半透明 |
| Tile-based | 画面分块处理 | 移动端友好、光源剔除高效 | 实现复杂 |
| Forward+ | Tile-based + Forward | 移动端友好、支持多光源 | 实现复杂 |
| Cluster-based | 按深度切分空间 | 支持上千光源 | 实现复杂 |
| Visibility Buffer | 只存储 Primitive ID + 重心坐标 + 深度 | 内存带宽小、支持 MSAA | 计算消耗大 |
Frame Graph:将渲染过程表示为有向无环图(DAG),自动管理资源和依赖关系,降低大型管线开发错误率
动画系统
动画概述
动画原理:利用人眼「视觉残留」效应(约1/24秒),快速切换静态画面模拟连续运动
游戏动画三大挑战
| 挑战 | 说明 |
|---|---|
| 交互式动态 | 无法预设玩家行为,需根据输入和环境灵活调整 |
| 实时性能 | 所有计算须在 30ms 内完成,动画数据量大需压缩优化 |
| 真实感 | 需动作匹配、物理动画、面部动画等技术实现高保真 |
2D/3D 动画技术
2D 动画
| 技术 | 说明 |
|---|---|
| 精灵动画 | 帧序列播放,可多视角绘制实现伪 3D 效果 |
| Live2D | 拆分部件 + 网格变形,无需 3D 模型即可生成生动 2D 动画 |
3D 动画技术对比
| 技术 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 刚性层级动画 | 部件作为刚体,层级约束 | 简单 | 关节处断裂不自然 |
| 顶点动画 | 直接控制网格顶点 | 最灵活 | 数据量大 |
| 形变目标动画 | 关键姿态 + 线性插值 | 适合面部表情 | 需存储多个姿态 |
| 骨骼蒙皮动画 | 网格绑定骨骼,顶点加权 | 自然流畅,主流方案 | 实现复杂 |
| 物理动画 | 基于物理法则模拟 | 真实感强 | 计算开销大 |
骨骼动画核心概念
骨骼结构:关节(Joint)组成树状层级,骨骼(Bone)是关节间的连接
关键关节
| 关节 | 作用 |
|---|---|
| 根关节 | 定义在角色两脚间,固定角色在地面上 |
| 骨盆关节 | 根关节的子关节,便于上下半身分离 |
| 挂载关节 | 武器/道具附着点,需保证朝向一致 |
绑定姿势:绑定骨骼前的网格姿态,现代 3A 游戏多用 A-pose(肩部更放松)而非 T-pose
骨骼姿态自由度:每个关节 9 个自由度 —— 朝向(3) + 位置(3) + 缩放(3)
3D 旋转数学
| 表示方法 | 说明 |
|---|---|
| 欧拉角 | 三个角度(Roll/Yaw/Pitch),直观但有万向锁问题 |
| 四元数 | 4 个分量表示旋转,无万向锁,插值平滑,游戏引擎主流 |
四元数插值
| 方法 | 特点 |
|---|---|
| NLERP | 归一化线性插值,速度快但角速度不恒定 |
| SLERP | 球面线性插值,角速度恒定但计算略复杂 |
| 混合策略 | 小角度用 NLERP,大角度用 SLERP,兼顾效率与质量 |
蒙皮动画实现
蒙皮矩阵:K = M^m_J(t) · (M^m_b(J))^-1,将顶点从绑定姿势变换到当前姿势
多关节加权蒙皮:V^m(t) = Σ Wi · V^m_i(t),顶点位置是各关节变换结果的加权和
动画运行时管线:提取关键帧 → 插值 → 计算蒙皮矩阵 → GPU 蒙皮
动画压缩
压缩策略
| 策略 | 说明 |
|---|---|
| 减少自由度 | 忽略缩放轨道,舍弃大部分平移轨道,只保留旋转 |
| 关键帧简化 | 移除可线性插值拟合的帧,Catmull-Rom 样条插值重建 |
| 数值量化 | 16 位精度存储平移,48 位存储四元数(省略最大分量) |
误差处理
| 方法 | 说明 |
|---|---|
| 自适应误差容限 | 末端关节精度要求高,越靠近根部精度要求越高 |
| 原位修正 | 在骨骼上选择点,计算补偿旋转叠加到压缩数据 |
| 伪顶点误差测量 | 在关节固定距离处设置虚拟顶点估算视觉误差 |
动画混合
线性插值混合:两个动画片段按权重 LERP 插值,实现平滑过渡
时间轴对齐:循环动画需保证步频一致、脚落地时间对齐,否则产生滑步
混合空间
| 类型 | 说明 |
|---|---|
| 一维混合空间 | 单参数控制(如速度),多采样点非均匀分布 |
| 二维混合空间 | 双参数控制(速度+方向),Delaunay 三角剖分 + 重心坐标插值 |
骨骼遮罩混合:对不同骨骼区域应用不同动画,如上半身鼓掌 + 下半身蹲下
加法混合:在基础动画上叠加差异片段(相对位姿),实现点头等叠加动作
动作状态机与动画树
动作状态机(ASM):将动作视为状态节点,通过转换条件在节点间切换
典型应用:跳跃动画拆分为起跳 → 浮空循环 → 落地三个状态,根据物理状态动态切换
动画混合树:将动画混合组织为表达式树,叶节点是动画片段/混合空间/ASM,非叶节点是混合操作
节点类型
| 类型 | 说明 |
|---|---|
| LERP 混合节点 | 线性插值混合多个输入 |
| 加法混合节点 | 叠加差异姿态 |
| 混合空间节点 | 1D/2D 混合空间作为输入 |
控制参数:通过命名变量控制混合权重,来源于游戏玩法系统
IK 反向运动学
IK vs FK
| 方式 | 说明 |
|---|---|
| FK(正向运动学) | 从根节点正向计算每个节点位置 |
| IK(反向运动学) | 已知末端位置,反推关节角度 |
典型应用:脚部在不平地面触地、手部抓取物体、头部注视目标
IK 算法
| 算法 | 原理 | 特点 |
|---|---|---|
| 双骨骼 IK | 余弦定理构造三角形 | 简单高效,适合腿部 |
| CCD | 循环坐标下降,逐关节旋转逼近目标 | 主流算法,支持约束 |
| FABRIK | 前向后向递推,在位置空间求解 | 收敛快,易处理约束 |
| 雅可比矩阵 | 迭代求解 Δθ = J⁻¹·Δs | 适合多末端效应器 |
关节约束:铰链关节(肘/膝)、球窝关节(肩/髋)等,忽略约束会导致不自然姿态
面部动画
FACS(面部动作编码系统):46 个基本动作单元(AU),组合表达各种表情
关键技术
| 技术 | 说明 |
|---|---|
| 形变目标动画 | 关键姿态 + 线性插值,存储相对中性表情的顶点偏移 |
| 骨骼面部动画 | 密集骨骼网络控制变形,适合大变形和捏脸系统 |
| UV 纹理动画 | 纹理切换实现表情,适合卡通风格 |
物理系统
物理对象与形状
物理对象类型
| 类型 | 特点 | 应用场景 |
|---|---|---|
| 静态对象 | 有碰撞但不移动 | 地面、墙壁、建筑物 |
| 动态对象 | 受力影响,运动状态动态变化 | 可推动的箱子、碎片 |
| 触发器 | 不阻塞对象,进入/退出时发出通知 | 开门区域、音效触发区 |
| 运动学角色 | 无视物理规则,由玩法逻辑控制 | 玩家角色控制器 |
物理形状选择原则
| 形状 | 参数 | 适用场景 |
|---|---|---|
| 球形 | 半径 | 球类游戏,最简单高效 |
| 胶囊体 | 半径+半高 | 人形角色,滚动平滑 |
| 盒子 | 三轴半长 | 规则物体,门、箱子 |
| 凸面网格 | 顶点+面 | 不规则凸形物体 |
| 三角网格 | 任意复杂形状 | 静态环境(不可用于动态角色) |
| 高度场 | 网格采样 | 大规模地形 |
形状使用原则:近似环绕即可、优先简单形状、能用球不用胶囊、能用盒不用网格
力与运动
牛顿运动定律
- 第一定律(惯性):无外力时
v(t+Δt) = v(t),x(t+Δt) = x(t) + v(t)Δt - 第二定律:
F = ma,加速度a = F/m
数值积分方法
| 方法 | 公式 | 特点 |
|---|---|---|
| 显式欧拉 | v₁ = v₀ + M⁻¹F₀Δt,x₁ = x₀ + v₀Δt |
简单高效,但能量累积发散 |
| 半隐式欧拉 | v₁ = v₀ + M⁻¹F₀Δt,x₁ = x₀ + v₁Δt |
更稳定,能量有阻尼 |
| Verlet | x(t+Δt) = 2x(t) - x(t-Δt) + a(t)Δt² |
无需显式存储速度,适合约束求解 |
刚体动力学
旋转表示
| 表示方法 | 特点 |
|---|---|
| 欧拉角 | 直观但有万向锁问题 |
| 四元数 | 无万向锁,插值平滑,主流方案 |
| 旋转矩阵 | 9个参数,有冗余 |
角动量与扭矩
- 角动量:
L = I·ω(I为转动惯量,ω为角速度) - 扭矩:
τ = r×F = dL/dt
碰撞检测
两阶段检测
| 阶段 | 目的 | 方法 |
|---|---|---|
| 粗检测 | 快速排除不可能碰撞的对象对 | BVH树、排序扫描法 |
| 窄检测 | 精确判断是否碰撞 | GJK算法、分离轴定理 |
GJK算法核心思想:利用Minkowski差集,判断原点是否在差集内部
分离轴定理:凸体若存在一条轴使投影不重叠,则不相交
场景查询
| 查询类型 | 说明 | 应用场景 |
|---|---|---|
| 射线投射 | 从一点沿方向发射射线检测碰撞 | 弹道计算、视线检测 |
| 扫描 | 有体积的形状沿路径移动检测 | 角色移动预测、碰撞避免 |
| 重叠检测 | 检测某区域内有哪些对象 | 爆炸范围、区域触发 |
角色控制器
运动学角色特性
- 不受物理规则影响(无重力、摩擦)
- 可以推动其他物体
- 加速与刹车几乎瞬间完成
关键功能
| 功能 | 说明 |
|---|---|
| 沿墙滑动 | 碰撞时计算切线方向,沿墙移动 |
| 坡度限制 | 设置最大爬坡角度,陡坡自动滑行 |
| 自动步进 | 自动跨过一定高度的台阶 |
| 体积更新 | 下蹲时动态调整碰撞体大小 |
| 平台跟随 | 站在移动平台上时同步运动 |
布娃娃系统
核心流程
- 骨骼映射:关键关节绑定刚体(胶囊体/球体)
- 关节约束:按解剖学设置约束(铰链、球窝、枢轴等)
- 动画驱动:每帧更新骨骼(中间关节用绑定姿态、活动关节用刚体姿态)
- 状态转换:运动学状态(动画驱动)↔ 动态状态(物理模拟)
- 动画融合:根据物理权重混合动画姿态和物理姿态
衣料模拟
模拟方法对比
| 方法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 基于动画 | 额外骨骼控制衣物 | 简单可控 | 不够真实 |
| 基于刚体 | 骨骼+刚体+约束 | 有物理感 | 计算量大 |
| 质量弹簧 | 质点+弹簧连接 | 实现简单 | 参数难调 |
| PBD | 基于位置的约束求解 | 稳定可控 | 需迭代求解 |
PBD核心思想:不计算力,直接通过约束投影修正位置,满足约束条件
效率与确定性
优化策略
| 策略 | 说明 |
|---|---|
| 孤岛优化 | 将场景划分为独立孤岛,稳定孤岛休眠 |
| 休眠机制 | 静止刚体停止计算,受力时唤醒 |
| CCD | 连续碰撞检测,防止高速物体穿透 |
确定性模拟要求
- 固定物理步长
- 确定性求解序列
- 浮点数一致性
粒子与声效系统
粒子基础
粒子属性:位置、速度、尺寸、颜色、生命周期
粒子生命周期:生成 → 老化(属性变化)→ 环境交互 → 死亡
发射器功能
- 定义生成规则(何时、何地生成)
- 定义模拟逻辑(物理行为)
- 定义渲染方式
生成模式
| 模式 | 特点 | 应用 |
|---|---|---|
| 连续模式 | 每帧可变生成速率 | 喷泉、火焰 |
| 爆发模式 | 一次性生成大量粒子 | 爆炸、冲击 |
粒子模拟
作用力
| 力 | 公式 | 说明 |
|---|---|---|
| 重力 | f = mg |
自然下落 |
| 粘性阻力 | f = -k_d·v |
速度方向相反,逐渐减速 |
| 风场 | f = k·v_wind |
沿风向移动 |
运动更新(显式积分)
v(t+Δt) = v(t) + a(t)·Δt
x(t+Δt) = x(t) + v(t+Δt)·Δt
参数变化:旋转、颜色变化、尺寸变化(如火焰从暗红→亮黄→暗淡)
粒子类型
| 类型 | 说明 | 应用 |
|---|---|---|
| 广告牌粒子 | 始终面向摄像机的面片 | 烟雾、火焰、光效 |
| 网格粒子 | 三维模型粒子 | 碎石、碎片、金属颗粒 |
| 带状粒子 | 拖尾轨迹,Catmull-Rom插值平滑 | 刀光、魔法轨迹、尾迹 |
粒子渲染
排序问题
| 排序方法 | 优点 | 缺点 |
|---|---|---|
| 全局排序 | 精确正确 | 性能消耗大 |
| 按发射器排序 | 性能好 | 可能排序错误 |
性能挑战:透明粒子产生大量Overdraw,一个屏幕大小粒子在4K下约800万像素
优化方案
| 方案 | 说明 |
|---|---|
| 低分辨率渲染 | 降采样渲染粒子,再上采样混合 |
| GPU粒子 | 全部计算在GPU完成,并行高效 |
| 深度缓冲碰撞 | 利用深度缓冲做粒子碰撞检测 |
GPU粒子
优势:并行处理数万粒子、避免CPU-GPU数据传输
关键技术
| 技术 | 说明 |
|---|---|
| 粒子列表管理 | 活跃列表、死亡列表、新增列表 |
| 并行归并排序 | GPU上对粒子按距离排序 |
| 深度缓冲碰撞 | 采样深度缓冲判断碰撞 |
声音基础
声音三要素
| 要素 | 说明 |
|---|---|
| 音量 | 声波振幅,响度感知 |
| 音高 | 声波频率,音调高低 |
| 音色 | 波形特征,区分不同声源 |
数字音频
| 概念 | 说明 |
|---|---|
| PCM | 脉冲编码调制,采样→量化→编码 |
| 采样率 | 44.1kHz/48kHz(香农定理:≥最高频率2倍) |
| 位深度 | 16bit/24bit,量化精度 |
音频格式对比
| 格式 | 质量 | 压缩 | 多声道 | 专利 |
|---|---|---|---|---|
| WAV | 高 | 无 | 支持 | 无 |
| MP3 | 低 | 有损 | 仅立体声 | 有 |
| OGG | 低 | 有损 | 支持 | 无 |
三维音频渲染
Listener属性:位置、速度、方向(类似相机)
空间感知三差异
| 差异 | 说明 |
|---|---|
| 音量差异 | 近耳音量大,远耳被阻挡音量小 |
| 时间差异 | 到达双耳时间差(十几毫秒可感知) |
| 音色差异 | 同一声音到达双耳音色微妙变化 |
等功率声像定位
Gain_left = sin(x)
Gain_right = cos(x)
Power_total = sin²(x) + cos²(x) = 1
衰减形状
| 形状 | 应用 |
|---|---|
| 球体 | 点声源,大多数情况 |
| 胶囊体 | 管道、线状声源 |
| 方盒 | 房间环境音 |
| 锥形 | 定向扬声器 |
混响组成
| 组成 | 说明 |
|---|---|
| 直达声 | 无反射直接到达 |
| 早期反射 | 几次反射后到达 |
| 后期混响 | 多次反射形成扩散声场 |
多普勒效应:f' = (V+V₀)/(V+V_s)·f,声源接近时频率升高,远离时降低
工具链系统
工具链概述
定义:用户与底层运行时之间的桥梁,协调不同背景用户协同工作
架构位置:位于用户工具层,连接引擎运行时与DCC工具
核心价值
- 连接DCC工具(Maya/Max/Blender/Photoshop等)
- 调和程序员、艺术家、设计师不同思维方式
- 复杂度通常超过运行时
GUI设计模式
| 模式 | 组成 | 特点 |
|---|---|---|
| MVC | Model-View-Controller | 单向数据流,View不直接改Model |
| MVP | Model-View-Presenter | View与Model完全解耦,Presenter中介 |
| MVVM | Model-View-ViewModel | 数据绑定,声明式UI |
即时模式 vs 保留模式
| 模式 | 特点 | 适用场景 |
|---|---|---|
| 即时模式 | 每帧重绘,无状态 | 简单工具、快速原型 |
| 保留模式 | 控件维护状态和数据 | 复杂工具系统 |
序列化与资产管理
文本 vs 二进制
| 格式 | 优点 | 缺点 |
|---|---|---|
| 文本(JSON/YAML/XML) | 可读、易调试 | 文件大、加载慢 |
| 二进制 | 文件小、加载快 | 需专用工具、难调试 |
资产引用:通过GUID建立引用关系,避免重复存储
数据继承:继承父数据,仅覆盖需要修改的属性,保持关联
世界编辑器
核心组件
| 组件 | 功能 |
|---|---|
| Viewport | 场景视图,WYSIWYG预览 |
| Outliner | 对象层级树状视图 |
| Details | 属性面板,Schema驱动 |
| Content Browser | 资源管理入口 |
选取方式
| 方式 | 说明 |
|---|---|
| 射线投射 | 从鼠标发射射线求交 |
| ID缓冲 | 渲染时写入对象ID,点击读取 |
地形编辑
| 工具 | 说明 |
|---|---|
| 高度笔刷 | 直接修改高度场 |
| 实例笔刷 | 批量散布装饰物(树木、石块) |
插件架构
插件组合模式
| 模式 | 说明 |
|---|---|
| 覆盖 | 新插件替换旧逻辑 |
| 分布式合并 | 多插件独立处理,结果合并 |
| 流水线 | 串联处理,逐步变换数据 |
| 洋葱圈 | 核心在中间,插件在入口出口两侧介入 |
叙事工具
序列器核心概念
| 概念 | 说明 |
|---|---|
| Sequence | 可播放的时间片段 |
| Track | 组织参与者(Actor、摄像机等) |
| Property Track | 绑定对象属性(Transform、灯光强度等) |
| Key Frame | 定义某时间点的属性值 |
| Interpolation | 关键帧之间的过渡方式 |
Gameplay与AI系统
Gameplay系统挑战
| 挑战 | 说明 |
|---|---|
| 跨系统协同 | 一个功能涉及动画、特效、音频、UI等多个系统 |
| 玩法多样 | 同一游戏可能包含多种截然不同的规则集合 |
| 迭代快速 | 玩法是迭代最快的部分,需支持快速验证与修改 |
事件机制
发布-订阅模式三组件
| 组件 | 职责 |
|---|---|
| 事件定义 | 类型+参数,描述”发生了什么” |
| 回调注册 | 建立事件类型→处理函数映射 |
| 事件分发 | 派送事件到对应回调集合 |
回调引用风险
| 方案 | 说明 |
|---|---|
| 强引用 | 回调存在则对象不释放,安全但易内存泄漏 |
| 弱引用 | 对象可正常释放,触发时检查有效性 |
分发方式
| 方式 | 优点 | 缺点 |
|---|---|---|
| 即时派发 | 简单 | 调用栈深、难以并行 |
| 事件队列 | 可控、可并行 | 可能引入一帧延迟 |
脚本系统
脚本语言价值
- 快速迭代(无需重编译引擎)
- 热更新(运行期替换逻辑)
- 隔离性(错误易恢复)
执行模型:脚本文本 → 字节码 → 虚拟机执行
对象管理范式
| 范式 | 说明 |
|---|---|
| 引擎主导 | 引擎管理原生对象生命周期,脚本持有引用需有效性检查 |
| 脚本主导 | 脚本运行时用GC管理对象 |
性能优化:JIT即时编译、约束写法、分层策略(高频逻辑放原生层)
可视化脚本
本质:仍是一门编程语言,需要变量、语句、表达式、控制流、函数
核心价值:降低表达逻辑门槛,类型不匹配等问题编辑期发现
代表:Unreal蓝图、Unity Visual Scripting
寻路系统
导航三步流程
- 地图表示:把可通行区域表达为数据结构
- 寻路:在结构上搜索路径
- 路径平滑:离散折线转化为自然轨迹
地图表示格式
| 格式 | 说明 | 优点 | 缺点 |
|---|---|---|---|
| 路径点网络 | 关键点+连接边 | 简单高效 | 覆盖精度依赖节点密度 |
| 网格 | 规则离散化格子 | 易更新、易调试 | 分辨率与存储矛盾 |
| NavMesh | 凸多边形覆盖可通行表面 | 高效、支持三维层叠 | 自动生成复杂 |
| SVO | 稀疏体素八叉树 | 支持3D空间导航 | 存储与搜索复杂度高 |
A*算法
f(n) = g(n) + h(n)
g(n): 从起点到n的已知代价
h(n): 从n到目标的估计代价(启发函数)
启发函数选择
| 场景 | 启发函数 |
|---|---|
| 网格(四方向) | 曼哈顿距离 |
| 网格(八方向) | 切比雪夫距离 |
| NavMesh | 欧几里得距离 |
路径平滑:漏斗算法(Funnel Algorithm),删除冗余拐点
NavMesh生成流程
体素化 → 标记可通行体素 → 计算距离场 → 区域分割(分水岭算法)→ 生成多边形
转向系统
三类原子能力
| 能力 | 输入 | 输出 |
|---|---|---|
| 追逐/逃离 | 自身位置、目标位置 | 线性加速度 |
| 速度匹配 | 目标速度、自身速度 | 线性加速度 |
| 对齐 | 目标朝向、自身朝向 | 角加速度 |
变体行为
| 行为 | 说明 |
|---|---|
| Pursue/Evade | 预测目标未来位置,再Seek/Flee |
| Wander | 随机游走目标点,持续Seek |
| Path Following | 目标点沿路径推进,不断Seek前瞻点 |
| Flow Field | 目标来自向量场,适合大规模群体 |
行为树
核心节点
| 节点 | 行为 |
|---|---|
| Sequence | 顺序执行,任一失败则失败 |
| Selector | 选择执行,任一成功则成功 |
| Parallel | 并行执行,按策略决定成功/失败 |
| Decorator | 修饰子节点行为(重复、反转等) |
执行特点:每帧从根节点遍历,响应式结构
HTN层次任务网络
核心概念
| 概念 | 说明 |
|---|---|
| 世界状态 | AI内部对关键属性的主观刻画 |
| 基础任务 | 前置条件+动作+效果 |
| 复合任务 | 多个方法(优先级+前置条件+任务链) |
| 领域 | 任务知识库,从根任务出发的层次结构 |
规划流程
- 选方法:按优先级检查前置条件
- 做分解:展开任务链,遇到复合任务继续分解
- 维护临时世界状态副本,假设动作成功更新
- 失败则回溯尝试下一方法
- 输出基础任务序列(计划)
重规划触发:无计划、计划执行完毕/失败、世界状态关键属性变化
GOAP目标导向行动规划
核心组件
| 组件 | 说明 |
|---|---|
| 目标集 | 前置条件+目标状态 |
| 动作集 | 前置条件+效果+成本 |
| 规划器 | 反向规划,从目标倒推到当前状态 |
反向规划流程
- 选定目标
- 构建未满足状态栈
- 选择能满足状态的动作
- 推入计划栈,递归处理前置条件
- 转化为图搜索(A*求最低成本路径)
vs HTN
| 对比 | HTN | GOAP |
|---|---|---|
| 知识组织 | 任务分解树 | 目标集+动作集 |
| 规划方向 | 正向分解 | 反向推导 |
| 灵活性 | 结构化、可控 | 更动态、环境适应性强 |
MCTS蒙特卡洛树搜索
四步流程
| 步骤 | 说明 |
|---|---|
| 选择 | 从根节点选择最有潜力的子节点 |
| 扩展 | 在选中节点添加新子节点 |
| 模拟 | 从新节点随机模拟到终局 |
| 回传 | 将模拟结果回传更新路径上所有节点 |
UCB公式:UCB = W/N + c·√(ln(N_parent)/N),平衡探索与利用
动画重定向
核心思想:将一个角色的动画迁移到另一个角色,实现动画复用
基本算法
| 轨迹类型 | 处理方式 |
|---|---|
| 旋转 | 来源于源动画,考虑相对旋转 |
| 平移 | 来源于目标骨骼,按比例补偿 |
| 缩放 | 来源于源动画 |
问题与解决
| 问题 | 解决方案 |
|---|---|
| 骨盆高度差异 | 运动缩放,根据骨盆高度调整 |
| 脚部悬空/穿模 | IK 锁定脚部 |
| 骨骼层级不同 | 共享骨骼中间层 + 插值映射 |
| 自网格穿透 | 动画师手动微调 |
网络系统
网络游戏核心挑战
| 挑战 | 说明 |
|---|---|
| 一致性 | 不同客户端对同一世界的理解必须在规则意义上收敛 |
| 可靠性 | 在不可靠传输上构建可靠体验 |
| 安全 | 作弊、信息泄露、账号盗用会破坏公平性 |
| 多样性 | 多平台、多终端、多子系统 |
| 复杂性 | 高并发、高可用、高性能 |
网络协议选择
| 协议 | 特点 | 适用场景 |
|---|---|---|
| TCP | 可靠有序、拥塞控制、面向连接 | 登录、支付、匹配等事务性系统 |
| UDP | 无连接、不保证可靠顺序、头部小 | 移动、瞄准、开火等高频交互 |
可靠 UDP:在 UDP 之上自定义传输语义,同时满足可靠性和时效性
可靠传输机制
| 机制 | 说明 |
|---|---|
| ARQ | 自动重传请求,丢了就补 |
| 滑动窗口 | 批量发送 + 批量确认,吃满带宽 |
| FEC | 前向纠错,用冗余换时延 |
ARQ 类型:
| 类型 | 说明 |
|---|---|
| 停止等待 | 窗口为1,实现简单但带宽利用率低 |
| 回退N帧 | 重传窗口内一段包,可能重传已到达的包 |
| 选择性重传 | 只重传丢失的包,带宽利用率高 |
时钟同步
RTT(往返时间):客户端发出请求到收到服务器响应的时间
NTP 算法:通过四个时间戳估计时钟偏移
offset = ((t1_s - t0_c) + (t2_s - t3_c)) / 2
网络拓扑
| 模式 | 特点 |
|---|---|
| 点对点 | 每个客户端直接通信,延迟低但难以防作弊 |
| 专用服务器 | 服务器权威,易防作弊但有额外延迟 |
| 监听服务器 | 一个客户端兼作服务器,节省成本但性能受限 |
位移同步
内插:用已知两个状态补中间状态,稳定平滑但更滞后
外推:根据历史状态预测未来,减少滞后但风险更高
PVB(投射速度融合):在固定融合时长内平滑拉回服务器状态
使用场景:
- 内插更常用于角色(高不确定性、高加速度)
- 外推更常用于载具(运动连续、预测可靠)
命中判定
| 方式 | 特点 |
|---|---|
| 客户端侧判定 | 体验好、反馈即时,但安全性差 |
| 服务器侧判定 | 权威安全,但需要延迟补偿 |
延迟补偿:服务器收到开枪消息后,回到射击者开枪那一刻的历史世界做判定
窥视者优势:从掩体探头时先看见对方,形成短暂先手窗口
MMO 架构
分层架构:
| 层级 | 职责 |
|---|---|
| 连接层 | Login Server、Gateway,管理用户连接与登录 |
| 业务层 | 大厅、战斗、社交、交易、匹配等业务服务 |
| 数据层 | 账号、角色、日志等数据持久化 |
AOI(兴趣区域):只同步玩家附近的对象,降低带宽和计算量
并行系统
并行编程基础
进程 vs 线程:
- 进程:独立内存区域,需要特殊机制交换信息
- 线程:共享内存,数据交换直接但易冲突
数据竞争:多线程同时访问同一内存,至少一次写入
解决方案:
| 方案 | 说明 |
|---|---|
| 锁 | 临界区保护,但有死锁、优先级反转问题 |
| 原子操作 | 硬件层面实现,无锁并行 |
内存模型:不同 CPU 架构对指令重排序的支持不同(x86 强、ARM 弱)
并行架构演进
| 架构 | 特点 | 问题 |
|---|---|---|
| 固定多线程 | 每个系统分配固定线程 | 负载不均衡、无法扩展 |
| Fork-Join | 工作线程池并行执行 | 需手动拆分、负载仍不均衡 |
| 任务图 | DAG 表示任务依赖 | 静态构建、动态增加复杂 |
任务系统
协程:轻量级执行上下文,yield 让出执行权,resume 恢复执行
| 类型 | 特点 |
|---|---|
| 有栈协程 | 独立运行时栈,支持嵌套 yield,内存开销大 |
| 无栈协程 | 无独立栈,切换快,但使用困难 |
Fiber-based Job System:
- 线程是执行单元,纤程是上下文
- 每个处理器核心对应一个线程,最小化上下文切换
- 作业在纤程上下文中执行
高级渲染技术
全局光照基础
渲染方程核心:出射辐射率 = 自发光 + 积分(入射光 × BRDF × 余弦项)
直接光照 vs 间接光照:
- 直接光照:光线从光源直接到达表面
- 间接光照:光线经过一次或多次反弹后到达表面
蒙特卡洛积分:通过随机采样近似积分,采样越多噪声越小
重要性采样:在重要的地方多采样,用更少样本获得更好结果
| 适用场景 | |
|---|---|
| 均匀分布 | 简单但效率低 |
| 余弦加权 | 漫反射表面 |
| GGX | 光滑表面 |
反射式阴影贴图 RSM
核心思想:从光源视角渲染,被照亮的表面成为间接光源
间接辐照度计算:E_p(x,n) = Φ_p * (n_p·(x-x_p)) * (n·(x_p-x)) / ||x-x_p||^4
锥体追踪:随机采样 RSM 像素而非遍历所有,约 400 个样本足够
动态全局光照 Lumen
核心目标:实时动态 GI,支持时间变化的光照环境
技术组合:
- 屏幕空间 GI(SSGI)
- 距离场 GI(DFGI)
- 体素 GI
- 卡片投射(Card Projection)
GPU 驱动几何管线
传统渲染瓶颈:
- CPU 负载过高(剔除、LoD 选择、资源绑定)
- GPU 空闲等待
- DrawCall 碎片化
GPU 驱动渲染:
- Compute Shader 做 GPU 通用计算
- Indirect Draw 让 GPU 生成 draw 参数
- 从 DrawPrimitive 到 DrawScene
网格簇渲染:
- 几何拆分成固定大小簇(如 64/128 三角形)
- GPU 侧完成簇级剔除
- 索引缓冲压缩,只渲染可见三角形
遮挡剔除
Hi-Z(层次深度缓冲):深度金字塔,快速测试包围盒
两阶段剔除:
- 第一阶段:上一帧深度剔除,渲染可能可见对象
- 第二阶段:本帧深度修正误判
阴影遮挡:用相机深度重投影剔除不可能产生可见阴影的对象
可见性缓冲
延迟渲染问题:G-buffer 过肥,显存带宽压力大
可见性缓冲:只存储 Primitive ID + 重心坐标 + 深度,着色时再解码
优势:内存带宽小、支持 MSAA
23.2 面试题精选
基础题
1. 游戏引擎和渲染引擎的区别
题目
游戏引擎和渲染引擎有什么区别?
深入解析
渲染引擎只负责画面输出,是游戏引擎的一个子系统。游戏引擎是”框架 + 运行时 + 工具链”的集合,包含渲染、物理、动画、AI、网络、音频、资源管理、工具链等几十个子系统。
真正的难点在于将这些系统在毫秒级帧预算内协同运行,而非单一模块的实现。
答题示例
渲染引擎只负责画面输出,是游戏引擎的一个子系统。
游戏引擎是”框架 + 运行时 + 工具链”的集合,包含渲染、物理、动画、AI 等几十个子系统。
真正的难点是多系统在帧预算内协同运行。
参考文章
- 1.游戏引擎导论.md
2. 游戏引擎分层架构
题目
为什么游戏引擎要采用分层架构?各层职责是什么?
深入解析
分层架构的核心目的是解耦和复用。上层依赖下层,禁止反向耦合,下层稳定、上层灵活。
各层职责:
| 层级 | 职责 | 特点 |
|---|---|---|
| 工具层 | 编辑器、蓝图等可视化工具 | 非实时,面向开发者 |
| 功能层 | 渲染、动画、物理等运行时功能 | Tick 驱动 |
| 资源层 | 资产导入、GUID 管理、生命周期调度 | 数据驱动 |
| 核心层 | 数学库、容器、内存管理 | 高性能基建 |
| 平台层 | RHI 图形接口 | 屏蔽平台差异 |
答题示例
分层架构的核心目的是解耦和复用。
工具层面向开发者,功能层 Tick 驱动,资源层管理资产,核心层提供高性能基建,平台层屏蔽差异。
上层依赖下层,禁止反向耦合。
参考文章
- 2.引擎架构分层.md
3. GUID 和 Handle 的作用
题目
GUID 和 Handle 在资源管理中分别解决什么问题?
深入解析
GUID(全局唯一标识符)解决”资产身份识别”问题:
- 资产移动、重命名后仍能被正确引用
- 脱离路径依赖,实现跨项目资产复用
Handle(句柄)解决”内存安全访问”问题:
- 上层不直接持有内存地址
- 资产卸载重载后句柄仍有效
- 避免悬空指针导致的崩溃
两者配合:GUID 负责磁盘层面的身份识别,Handle 负责内存层面的安全访问。
答题示例
GUID 解决”资产身份识别”问题,资产移动重命名后仍能被正确引用。
Handle 解决”内存安全访问”问题,资产卸载重载后句柄仍有效,避免悬空指针。
GUID 负责磁盘层面,Handle 负责内存层面。
参考文章
- 2.引擎架构分层.md
4. 游戏渲染与离线渲染的区别
题目
游戏渲染和离线渲染(如电影特效)有什么本质区别?
深入解析
| 维度 | 游戏渲染 | 离线渲染 |
|---|---|---|
| 时间约束 | 严格帧预算(16.6ms/33ms) | 无限制,可数小时渲染一帧 |
| 目标 | 视觉感知正确即可 | 物理绝对正确 |
| 算法选择 | 近似但快,可接受瑕疵 | 精确但慢,追求真实 |
| 光照 | 简化模型 + 预计算 | 全局光照、光线追踪 |
游戏渲染的核心是在有限算力下做取舍,而非追求物理正确。
答题示例
游戏渲染有严格帧预算(16.6ms/帧),追求视觉感知正确,算法选择近似但快。
离线渲染无时间限制,追求物理绝对正确,可用光线追踪等精确算法。
游戏渲染的核心是在有限算力下做取舍。
参考文章
- 4.游戏引擎中的渲染实践.md
5. 渲染管线基本流程
题目
简述渲染管线的基本流程。
深入解析
渲染管线的基本流程:顶点数据 → 顶点变换(MVP矩阵)→ 图元组装 → 光栅化 → 片段着色 → 像素输出
各阶段作用:
- 顶点变换:将顶点从模型空间变换到裁剪空间
- 图元组装:将顶点组装成三角形等图元
- 光栅化:确定三角形覆盖哪些像素,生成片段
- 片段着色:计算每个片段的颜色(纹理采样、光照计算)
- 像素输出:深度测试、模板测试、混合后写入帧缓冲
答题示例
顶点数据 → 顶点变换 → 图元组装 → 光栅化 → 片段着色 → 像素输出。
顶点变换将顶点从模型空间变换到裁剪空间,光栅化生成片段,片段着色计算颜色,最后经深度测试写入帧缓冲。
参考文章
- 4.游戏引擎中的渲染实践.md
6. Draw Call 对性能的影响
题目
什么是 Draw Call?为什么 Draw Call 过多会影响性能?
深入解析
Draw Call 是 CPU 向 GPU 发送的一次绘制命令。每次 Draw Call 需要 CPU 准备渲染状态、设置参数、提交命令,这些操作在 CPU 端执行。
Draw Call 过多影响性能的原因:
- CPU 端开销大,GPU 可能处于空闲等待状态
- 状态切换(Shader、纹理、材质)开销累积
- 驱动层验证和转换开销
优化方法:
- 批处理(Batching):合并相同材质的物体
- 实例化渲染(Instancing):一次绘制多个相同网格
- 减少材质种类:降低状态切换
答题示例
Draw Call 是 CPU 向 GPU 发送的一次绘制命令。
过多影响性能是因为 CPU 端开销大,GPU 可能空闲等待,状态切换开销累积。
优化方法:批处理、实例化渲染、减少材质种类。
参考文章
- 4.游戏引擎中的渲染实践.md
7. 骨骼蒙皮动画的优势
题目
骨骼蒙皮动画相比刚性层级动画有什么优势?
深入解析
刚性层级动画:每个部件作为刚体,关节处断裂不自然,早期 3D 游戏使用。
骨骼蒙皮动画:网格绑定到骨骼,每个顶点可受多个关节影响(加权蒙皮),关节处平滑过渡,自然流畅。
蒙皮动画是现代游戏的主流方案,能实现真实的皮肤变形效果。
答题示例
刚性层级动画每个部件作为刚体,关节处断裂不自然。
骨骼蒙皮动画每个顶点可受多个关节影响,关节处平滑过渡。
蒙皮动画是现代游戏主流方案,能实现真实的皮肤变形效果。
参考文章
- 8.游戏引擎的动画技术基础.md
8. 四元数与欧拉角
题目
为什么游戏引擎用四元数表示旋转而不是欧拉角?
深入解析
欧拉角问题:
- 万向锁(Gimbal Lock):特定角度下丢失一个自由度
- 插值不平滑:三个角度独立插值会产生奇怪的旋转路径
- 旋转顺序依赖:不同顺序结果不同
四元数优势:
- 无万向锁
- 插值平滑(SLERP)
- 存储紧凑(4个分量)
- 旋转组合高效(四元数乘法)
答题示例
欧拉角有万向锁问题,插值不平滑,旋转顺序依赖。
四元数无万向锁,插值平滑(SLERP),存储紧凑,旋转组合高效。
游戏引擎用四元数避免万向锁并获得平滑插值。
参考文章
- 8.游戏引擎的动画技术基础.md
进阶题
1. 固定步长与可变步长 Tick
题目
固定步长 Tick 和可变步长 Tick 各有什么优缺点?引擎如何选择?
深入解析
固定步长:
- 优点:逻辑更新频率恒定(如 60Hz),物理模拟稳定、确定性高
- 缺点:帧率波动时可能出现卡顿或画面撕裂
可变步长:
- 优点:跟随实际帧时间,画面流畅
- 缺点:物理模拟不稳定、难以实现确定性回放
现代引擎方案:固定逻辑步长 + 插值渲染
- 逻辑以固定频率更新保证稳定性
- 渲染根据实际帧时间对前后两帧逻辑状态插值
- 兼顾稳定与流畅
答题示例
固定步长物理模拟稳定、确定性高,但帧率波动可能卡顿。
可变步长画面流畅,但物理模拟不稳定、难以确定性回放。
现代引擎采用固定逻辑步长 + 插值渲染,兼顾稳定与流畅。
参考文章
- 2.引擎架构分层.md
- 3.如何构建游戏世界.md
2. 组件化 Tick 与对象化 Tick
题目
组件化 Tick 相比对象化 Tick 有什么优势?为什么现代引擎倾向组件化?
深入解析
对象化 Tick:
- 按对象顺序遍历更新内部组件
- 直观易调试
- 无法并行、Cache 命中率低
组件化 Tick:
- 按组件类型批量更新
- 同类型组件数据连续存储
- 可并行执行、Cache 友好
现代引擎选择组件化 Tick 的原因:
- 多核 CPU 并行效率更高
- 数据局部性更好
- 适合 Job System 调度
- 同时保留对象化 Tick 兼容接口供特殊需求使用
答题示例
对象化 Tick 直观易调试,但无法并行、Cache 命中率低。
组件化 Tick 按类型批量更新,数据连续存储,可并行、Cache 友好。
现代引擎选择组件化是因为多核并行效率高,适合 Job System 调度。
参考文章
- 3.如何构建游戏世界.md
3. 游戏确定性保证
题目
如何保证游戏运行的确定性(Deterministic)?为什么这很重要?
深入解析
确定性指相同输入必然产生相同输出,是精彩回放、多人同步、测试复现的基础。
保证方法:
- 固定步长逻辑:避免浮点误差累积
- 邮局模式事件:事件先存队列,隔帧统一分发,避免即时回调的时序问题
- Pre/Post Tick 分阶段:拆分为预准备→主逻辑→后同步
- 父子对象规则:父对象先 Tick、子对象后 Tick
- 确定性随机:使用确定性随机种子
答题示例
确定性是精彩回放、多人同步、测试复现的基础。
保证方法:固定步长逻辑避免浮点误差,邮局模式事件避免时序问题,Pre/Post Tick 分阶段,父子对象有序 Tick,确定性随机种子。
参考文章
- 3.如何构建游戏世界.md
4. Forward 与 Deferred Rendering
题目
Forward Rendering 和 Deferred Rendering 各有什么优缺点?如何选择?
深入解析
| 特性 | Forward | Deferred |
|---|---|---|
| 光源处理 | 按物体×光源计算 | 按像素处理光源 |
| 多光源性能 | O(物体×光源),差 | O(像素×光源),优 |
| MSAA | 支持 | 困难(G-Buffer限制) |
| 半透明 | 支持 | 需额外Forward Pass |
| 内存带宽 | 低 | 高(G-Buffer) |
选择建议:
- 光源少的简单场景用 Forward
- 光源多的复杂场景用 Deferred
- 移动端优先 Forward+ 或 Tile-based
答题示例
Forward 光源少时性能好,支持 MSAA 和半透明,但多光源性能差。
Deferred 多光源性能好,但 MSAA 困难,内存带宽高。
光源少用 Forward,光源多用 Deferred,移动端优先 Forward+。
参考文章
- 7.游戏中渲染管线和后处理及其他.md
5. PBR 材质模型
题目
PBR 材质模型相比传统 Blinn-Phong 有什么优势?DFG 三项分别是什么?
深入解析
PBR(Physically Based Rendering)基于物理规律,能量守恒,材质参数直观,不同光照环境下表现一致。
DFG 三项:
- D(Normal Distribution Function):法线分布函数,决定高光形状和大小,受粗糙度影响。常用 GGX/Trowbridge-Reitz。
- F(Fresnel Term):菲涅尔项,掠射角反射更强,受金属度影响。常用 Schlick 近似。
- G(Geometry Function):几何衰减项,描述微表面自遮挡和自阴影。常用 Smith 方法。
答题示例
PBR 基于物理规律,能量守恒,材质参数直观,不同光照环境表现一致。
D 是法线分布函数决定高光形状,F 是菲涅尔项掠射角反射更强,G 是几何衰减项描述自遮挡。
常用 GGX、Schlick 近似、Smith 方法。
参考文章
- 5.游戏渲染中光和材质的数学魔法.md
6. 全局光照实现
题目
什么是全局光照(GI)?游戏引擎如何实现 GI?
深入解析
全局光照 = 直接光照 + 间接光照(光线在场景中多次反弹)。GI 让场景更真实,阴影区域也有环境光。
实现方法:
| 类型 | 方法 | 适用场景 |
|---|---|---|
| 预计算 | LightMap | 静态场景烘焙 |
| 预计算 | Light Probe | 动态物体插值 |
| 预计算 | Reflection Probe | 镜面反射 |
| 实时 | SSAO | 屏幕空间环境光遮蔽 |
| 实时 | SSGI | 屏幕空间全局光照 |
| 实时 | RTX GI | 光线追踪 |
| 混合 | 预计算 + 实时补充 | 大多数游戏 |
答题示例
全局光照 = 直接光照 + 间接光照,让场景更真实。
预计算有 LightMap、Light Probe、Reflection Probe。
实时有 SSAO、SSGI、RTX GI。大多数游戏用混合方案。
参考文章
- 5.游戏渲染中光和材质的数学魔法.md
7. 动画压缩策略
题目
动画压缩有哪些策略?如何平衡压缩率和精度?
深入解析
压缩策略:
- 减少自由度:忽略缩放轨道,舍弃大部分平移轨道,只保留旋转
- 关键帧简化:移除可线性插值拟合的帧,Catmull-Rom 样条插值重建
- 数值量化:16 位存储平移,48 位存储四元数(省略最大分量)
精度保障:
- 自适应误差容限:末端关节精度要求高,越靠近根部精度要求越高
- 伪顶点误差测量:在关节固定距离处设置虚拟顶点估算视觉误差
- 原位修正:计算补偿旋转叠加到压缩数据
答题示例
压缩策略:减少自由度只保留旋转,关键帧简化,数值量化。
精度保障:自适应误差容限,末端关节精度要求高;伪顶点误差测量估算视觉误差;原位修正补偿旋转。
参考文章
- 8.游戏引擎的动画技术基础.md
8. IK 与 FK 的区别
题目
IK 和 FK 有什么区别?IK 在游戏中有什么应用?
深入解析
FK(正向运动学):从根节点正向计算每个节点位置,适合预设动画播放。
IK(反向运动学):已知末端位置,反推关节角度,适合动态约束场景。
IK 应用:
- 脚部在不平地面触地
- 手部抓取物体
- 头部注视目标
- 角色攀爬时四肢附着墙壁
常用算法:
- 双骨骼 IK:简单高效
- CCD:主流,支持约束
- FABRIK:收敛快
- 雅可比矩阵:多末端效应器
答题示例
FK 从根节点正向计算位置,适合预设动画播放。
IK 已知末端位置反推关节角度,适合动态约束场景。
IK 应用:脚部触地、手部抓取、头部注视、攀爬附着。常用 CCD 算法。
参考文章
- 9.动画树IK和表情动画.md
9. 物理形状选择原则
题目
游戏物理引擎中常用的碰撞形状有哪些?选择原则是什么?
深入解析
常用形状:
| 形状 | 参数 | 适用场景 |
|---|---|---|
| 球形 | 半径 | 球类游戏,最简单高效 |
| 胶囊体 | 半径+半高 | 人形角色,滚动平滑 |
| 盒子 | 三轴半长 | 规则物体,门、箱子 |
| 凸面网格 | 顶点+面 | 不规则凸形物体 |
| 三角网格 | 任意复杂形状 | 静态环境(不可用于动态角色) |
| 高度场 | 网格采样 | 大规模地形 |
选择原则:
- 近似环绕即可,无需精确匹配
- 优先简单形状,能用球不用胶囊,能用盒不用网格
- 三角网格仅用于静态环境,动态角色禁用
答题示例
常用形状:球形、胶囊体、盒子、凸面网格、三角网格、高度场。
选择原则:近似环绕即可,优先简单形状,能用球不用胶囊,能用盒不用网格。三角网格仅用于静态环境。
参考文章
- 10.游戏引擎中物理系统的基础理论和算法.md
10. 碰撞检测两阶段流程
题目
物理引擎的碰撞检测为什么要分两阶段?各阶段用什么方法?
深入解析
分两阶段的原因:N 个对象两两检测是 O(N²) 复杂度,直接精确检测性能无法接受。
两阶段流程:
| 阶段 | 目的 | 方法 | 复杂度 |
|---|---|---|---|
| 粗检测 | 快速排除不可能碰撞的对象对 | BVH树、排序扫描法 | O(N log N) |
| 窄检测 | 精确判断是否碰撞 | GJK算法、分离轴定理 | 仅对候选对 |
粗检测方法:
- BVH树:层次包围盒,动态更新
- 排序扫描法:按轴投影排序,检测区间重叠
窄检测方法:
- GJK算法:利用 Minkowski 差集判断原点是否在差集内部
- 分离轴定理:凸体若存在一条轴使投影不重叠,则不相交
答题示例
分两阶段是因为直接两两检测是 O(N²) 复杂度。
粗检测用 BVH 树或排序扫描法快速排除不可能碰撞的对,复杂度 O(N log N)。
窄检测用 GJK 算法或分离轴定理精确判断,仅对候选对执行。
参考文章
- 10.游戏引擎中物理系统的基础理论和算法.md
11. 角色控制器特性
题目
游戏中的角色控制器为什么通常用运动学角色而不是动态刚体?
深入解析
运动学角色特性:
- 不受物理规则影响(无重力、摩擦)
- 可以推动其他物体
- 加速与刹车几乎瞬间完成
- 由玩法逻辑控制移动
动态刚体的问题:
- 受物理规则约束,难以实现精确控制
- 容易被其他物体影响(被撞飞、卡住)
- 摩擦和惯性导致手感不理想
关键功能:
- 坡度限制:太陡的坡无法攀爬
- 自动步进:小台阶自动迈过
- 体积更新:蹲下时碰撞体缩小
- 站在移动平台上:跟随平台移动
答题示例
运动学角色不受物理规则影响,由玩法逻辑控制,加速刹车瞬间完成,手感更好。
动态刚体受物理规则约束,难以精确控制,容易被撞飞或卡住。
关键功能:坡度限制、自动步进、体积更新、站在移动平台上。
参考文章
- 11.物理系统高级应用.md
12. 布娃娃系统原理
题目
布娃娃系统是如何实现的?与动画如何融合?
深入解析
实现原理:
- 将骨骼映射到刚体(约 15-20 个刚体)
- 每个关节添加约束(铰链约束、锥形约束等)
- 约束限制旋转角度和范围
约束属性:
- 关节类型:铰链、万向节、球窝
- 角度限制:最小/最大角度
- 刚度:抵抗变形的力度
与动画融合:
- 正常状态播放动画
- 死亡/击倒时切换到布娃娃物理
- 可混合:部分骨骼物理驱动,部分动画驱动
- 站起动画:从布娃娃姿态平滑过渡到预设动画
答题示例
布娃娃将骨骼映射到刚体(约 15-20 个),每个关节添加约束限制旋转角度。
约束属性包括关节类型、角度限制、刚度。
与动画融合:正常播放动画,死亡时切换物理,可混合驱动,站起时平滑过渡。
参考文章
- 11.物理系统高级应用.md
13. 粒子系统基本组成
题目
粒子系统的基本组成有哪些?粒子生命周期是怎样的?
深入解析
粒子属性:位置、速度、尺寸、颜色、生命周期
粒子系统组成:
- 发射器:定义生成规则(何时、何地、生成多少)
- 模拟器:更新粒子状态(物理行为、属性变化)
- 渲染器:定义渲染方式(广告牌、网格、带状)
粒子生命周期:
- 生成:从发射器创建,初始化属性
- 老化:属性随时间变化(颜色渐变、尺寸衰减)
- 环境交互:与场景碰撞、受力影响
- 死亡:生命周期结束,回收粒子
答题示例
粒子属性:位置、速度、尺寸、颜色、生命周期。
系统组成:发射器定义生成规则,模拟器更新状态,渲染器定义渲染方式。
生命周期:生成 → 老化(属性变化)→ 环境交互 → 死亡。
参考文章
- 12.游戏引擎中的粒子和声效系统.md
14. GPU 粒子优势
题目
GPU 粒子相比 CPU 粒子有什么优势?如何实现?
深入解析
优势:
- 大规模粒子:CPU 处理数万粒子上限,GPU 可处理百万级
- 并行计算:每个粒子独立计算,GPU 天然适合
- 减少数据传输:粒子数据不回传 CPU,直接在 GPU 渲染
实现框架:
- 粒子列表:存储所有粒子数据的结构化缓冲区
- 并行归并排序:用于深度排序
- 深度缓冲区碰撞:粒子与场景碰撞检测
关键技术:
- Compute Shader 执行物理模拟
- Append/Consume Buffer 管理粒子生死
- 深度缓冲区采样实现碰撞
答题示例
GPU 粒子可处理百万级粒子,CPU 仅数万。GPU 并行计算天然适合粒子模拟,减少 CPU-GPU 数据传输。
实现:Compute Shader 执行模拟,Append/Consume Buffer 管理生死,深度缓冲区采样实现碰撞。
参考文章
- 12.游戏引擎中的粒子和声效系统.md
15. 三维音频渲染
题目
游戏引擎如何实现三维音频?有哪些关键技术?
深入解析
三维音频核心:让声音随听者与声源的位置关系变化。
关键技术:
| 技术 | 说明 |
|---|---|
| 声像定位 | 根据声源位置调整左右声道音量比 |
| 衰减形状 | 球形、胶囊体、盒子衰减区域 |
| 阻碍与遮挡 | 墙壁阻挡声音传播,降低音量和高频 |
| 混响 | 模拟空间反射,不同材质不同混响 |
| 多普勒效应 | 声源接近时频率升高,远离时降低 |
Listener 属性:位置、朝向、速度
答题示例
三维音频让声音随听者与声源的位置关系变化。
关键技术:声像定位调整声道音量比,衰减形状定义声音范围,阻碍遮挡模拟墙壁阻挡,混响模拟空间反射,多普勒效应模拟运动频率变化。
参考文章
- 12.游戏引擎中的粒子和声效系统.md
16. 工具链 GUI 设计模式
题目
工具链开发中常用的 GUI 设计模式有哪些?各有什么特点?
深入解析
常用模式:
| 模式 | 特点 | 适用场景 |
|---|---|---|
| MVC | Model-View-Controller,经典分离 | 传统桌面应用 |
| MVP | Model-View-Presenter,View 不直接访问 Model | 需要测试的场景 |
| MVVM | Model-View-ViewModel,数据绑定 | 现代框架(WPF、Unity UI) |
即时模式 vs 保留模式:
| 模式 | 特点 | 代表 |
|---|---|---|
| 即时模式 | 每帧重新构建 UI,代码驱动 | Dear ImGui |
| 保留模式 | UI 元素持久存在,数据驱动 | Qt、WPF |
即时模式适合工具开发迭代快,保留模式适合复杂界面。
答题示例
MVC 经典分离,MVP 便于测试,MVVM 数据绑定适合现代框架。
即时模式每帧重建 UI,代码驱动,适合工具开发。保留模式 UI 元素持久,数据驱动,适合复杂界面。
参考文章
- 13.引擎工具链基础.md
17. 序列化格式选择
题目
游戏引擎中序列化格式如何选择?文本格式和二进制格式各有什么优缺点?
深入解析
文本格式(JSON、XML、YAML):
- 优点:可读性好、易于调试、支持版本控制
- 缺点:解析慢、文件大、不适合运行时
二进制格式:
- 优点:解析快、文件小、运行时高效
- 缺点:不可读、难以调试、版本兼容复杂
选择建议:
- 配置文件、原型数据:文本格式
- 运行时资产、大规模数据:二进制格式
- 支持相互转换:开发用文本,发布转二进制
资产引用:
- GUID 标识资产身份
- 路径仅用于调试显示
答题示例
文本格式可读性好、易调试,但解析慢、文件大。二进制格式解析快、文件小,但不可读。
配置文件用文本,运行时资产用二进制。开发用文本,发布转二进制。资产引用用 GUID。
参考文章
- 13.引擎工具链基础.md
18. 事件机制设计
题目
游戏引擎的事件机制如何设计?发布-订阅模式有什么注意事项?
深入解析
发布-订阅模式三组件:
- 事件定义:描述事件类型和数据
- 发布者:触发事件
- 订阅者:响应事件
分发方式:
| 方式 | 特点 |
|---|---|
| 即时分发 | 事件触发时立即回调 |
| 延迟分发 | 事件存入队列,隔帧统一分发 |
注意事项:
- 回调引用风险:订阅者销毁前必须取消订阅
- 即时回调时序问题:可能导致状态不一致
- 延迟分发保证确定性:适合回放和多人同步
答题示例
发布-订阅三组件:事件定义、发布者、订阅者。
即时分发立即回调,延迟分发存队列隔帧分发。
注意:订阅者销毁前必须取消订阅,即时回调可能导致时序问题,延迟分发保证确定性。
参考文章
- 15.游戏引擎Gameplay玩法系统基础.md
19. 脚本语言在引擎中的作用
题目
为什么游戏引擎需要脚本语言?脚本与原生代码如何分工?
深入解析
脚本语言价值:
- 快速迭代:无需重新编译引擎
- 安全沙箱:限制危险操作,防止崩溃
- 易于学习:设计师也能参与开发
- 热重载:运行时修改立即生效
分工原则:
| 类型 | 实现方式 | 示例 |
|---|---|---|
| 性能关键 | 原生代码 | 渲染、物理、动画核心 |
| 玩法逻辑 | 脚本 | 技能、任务、UI 交互 |
| 工具扩展 | 脚本 | 编辑器插件 |
执行模型:
- 解释执行:Lua、Python
- JIT 编译:LuaJIT
- 虚拟机:C#(Unity)、Blueprint(UE)
答题示例
脚本语言支持快速迭代、安全沙箱、易于学习、热重载。
分工:性能关键用原生代码,玩法逻辑用脚本,工具扩展用脚本。
执行模型有解释执行、JIT 编译、虚拟机。
参考文章
- 15.游戏引擎Gameplay玩法系统基础.md
20. A* 寻路算法
题目
A* 寻路算法的原理是什么?如何优化?
深入解析
核心公式:f(n) = g(n) + h(n)
- g(n):起点到 n 的实际代价
- h(n):n 到终点的估计代价(启发函数)
- f(n):总估计代价
算法流程:
- 开放列表放起点
- 取 f 值最小的节点
- 若是终点则结束,否则扩展邻居
- 更新邻居的 g、h、f 值
- 重复直到找到终点或开放列表为空
优化方法:
- 启发函数选择:曼哈顿距离、欧几里得距离、切比雪夫距离
- 二叉堆/优先队列优化开放列表
- 双向 A*:从起点和终点同时搜索
- 路径平滑:移除不必要的拐点
答题示例
核心公式 f(n) = g(n) + h(n),g 是实际代价,h 是估计代价。
流程:取 f 最小的节点,扩展邻居,更新代价,重复直到找到终点。
优化:选择合适启发函数,二叉堆优化开放列表,双向搜索,路径平滑。
参考文章
- 16.游戏引擎Gameplay玩法系统基础AI.md
21. 行为树基本结构
题目
行为树的基本节点类型有哪些?执行特点是什么?
深入解析
核心节点类型:
| 节点 | 行为 | 返回值 |
|---|---|---|
| Sequence | 顺序执行子节点 | 任一失败则失败,全部成功才成功 |
| Selector | 选择执行子节点 | 任一成功则成功,全部失败才失败 |
| Parallel | 并行执行子节点 | 按策略决定成功/失败 |
| Decorator | 修饰子节点行为 | 重复、反转、条件判断等 |
| Action | 执行具体行为 | 成功/失败/运行中 |
| Condition | 检查条件 | 成功/失败 |
执行特点:
- 每帧从根节点遍历
- 响应式结构,可中断当前行为
- 状态可保存,下次继续执行
答题示例
核心节点:Sequence 顺序执行,Selector 选择执行,Parallel 并行执行,Decorator 修饰行为,Action 执行具体行为,Condition 检查条件。
执行特点:每帧从根节点遍历,响应式结构可中断,状态可保存。
参考文章
- 17.游戏引擎Gameplay玩法系统高级AI.md
22. HTN 与 GOAP 对比
题目
HTN 和 GOAP 有什么区别?各适合什么场景?
深入解析
| 对比 | HTN | GOAP |
|---|---|---|
| 知识组织 | 任务分解树 | 目标集 + 动作集 |
| 规划方向 | 正向分解 | 反向推导 |
| 灵活性 | 结构化、可控 | 更动态、环境适应性强 |
| 可预测性 | 高,行为符合设计预期 | 低,可能产生意外行为 |
| 调试难度 | 较低,任务链清晰 | 较高,需追踪规划过程 |
适用场景:
- HTN:需要可控行为的 NPC、敌人 AI
- GOAP:需要动态适应环境的 AI、策略游戏
答题示例
HTN 正向分解任务树,结构化可控,适合需要可控行为的 NPC。
GOAP 反向从目标推导,动态适应性强,适合需要灵活应对环境的 AI。
HTN 可预测性高,GOAP 可能产生意外行为。
参考文章
- 17.游戏引擎Gameplay玩法系统高级AI.md
23. MCTS 蒙特卡洛树搜索
题目
MCTS 的四步流程是什么?UCB 公式如何平衡探索与利用?
深入解析
四步流程:
| 步骤 | 说明 |
|---|---|
| 选择 | 从根节点选择最有潜力的子节点,直到叶子节点 |
| 扩展 | 在选中节点添加新子节点 |
| 模拟 | 从新节点随机模拟到终局 |
| 回传 | 将模拟结果回传更新路径上所有节点 |
UCB 公式:UCB = W/N + c·√(ln(N_parent)/N)
- W:节点获胜次数
- N:节点访问次数
- c:探索常数
- 第一项利用:选择胜率高的节点
- 第二项探索:选择访问少的节点
应用场景:回合制游戏 AI、围棋等完美信息博弈
答题示例
四步流程:选择最有潜力节点 → 扩展新子节点 → 随机模拟到终局 → 回传更新结果。
UCB 公式平衡探索与利用:第一项选择胜率高的节点(利用),第二项选择访问少的节点(探索)。
适用于回合制游戏 AI、围棋等完美信息博弈。
参考文章
- 17.游戏引擎Gameplay玩法系统高级AI.md
24. TCP 与 UDP 选择
题目
游戏网络开发中 TCP 和 UDP 如何选择?
深入解析
TCP 特点:可靠有序、拥塞控制、面向连接,但延迟波动大
UDP 特点:无连接、不保证可靠顺序、头部小,但需上层处理可靠性
选择原则:
| 场景 | 推荐协议 | 原因 |
|---|---|---|
| 登录、支付、匹配 | TCP 或可靠 UDP | 事务性系统需要可靠性 |
| 移动、瞄准、开火 | UDP | 高频交互需要低延迟 |
可靠 UDP:在 UDP 之上自定义传输语义,同时满足可靠性和时效性
答题示例
TCP 可靠但延迟波动大,适合事务性系统。UDP 快但不可靠,适合高频交互。
登录支付用 TCP,移动开火用 UDP。可靠 UDP 在 UDP 上层实现可靠性。
参考文章
- 18.网络游戏的架构基础.md
25. 延迟补偿原理
题目
什么是延迟补偿?如何解决命中判定问题?
深入解析
问题:射击者看到的敌人位置是经过插值的”过去位置”,服务器是”当前权威位置”,两者不一致
延迟补偿:服务器收到开枪消息后,回到射击者开枪那一刻的历史世界做判定
实现要点:
- 服务器保存一段时间的历史状态快照
- 回溯时间 = 当前时间 - 网络延迟 - 客户端插值偏移
- 在历史状态上执行射线/弹道检测
窥视者优势:从掩体探头时先看见对方,形成短暂先手窗口,是延迟的固有现象
答题示例
延迟补偿是服务器收到开枪消息后,回到射击者开枪那一刻的历史世界做判定。
实现要点:保存历史快照,计算回溯时间,在历史状态上检测。
窥视者优势是延迟的固有现象,探头时先看见对方。
参考文章
- 19.网络游戏的进阶架构.md
26. 位移同步策略
题目
内插和外推各有什么优缺点?分别适合什么场景?
深入解析
| 方法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 内插 | 用已知两个状态补中间状态 | 稳定平滑 | 更滞后 |
| 外推 | 根据历史状态预测未来 | 减少滞后 | 预测可能错误 |
PVB(投射速度融合):在固定融合时长内平滑拉回服务器状态
使用场景:
- 内插更常用于角色:高不确定性、高加速度,外推容易预测错
- 外推更常用于载具:运动连续、预测可靠
答题示例
内插用已知状态补中间,稳定平滑但更滞后。外推根据历史预测未来,减少滞后但可能预测错误。
角色用内插,载具用外推。PVB 在固定时长内平滑拉回服务器状态。
参考文章
- 19.网络游戏的进阶架构.md
27. MMO 服务器架构
题目
MMO 服务器通常采用什么架构?各层职责是什么?
深入解析
分层架构:
| 层级 | 组件 | 职责 |
|---|---|---|
| 连接层 | Login Server、Gateway | 管理用户连接与登录,隔离内外网 |
| 业务层 | 大厅、战斗、社交、交易、匹配 | 具体业务逻辑 |
| 数据层 | 角色服、数据库 | 数据持久化存储 |
AOI(兴趣区域):只同步玩家附近的对象,降低带宽和计算量
关键设计:
- Gateway 可水平扩展,玩家只与网关通信
- 角色服集中管理玩家数据,避免数据四散
答题示例
连接层管理连接登录,业务层处理具体逻辑,数据层持久化存储。
Gateway 隔离内外网可水平扩展,角色服集中管理玩家数据。
AOI 只同步玩家附近对象,降低带宽和计算量。
参考文章
- 19.网络游戏的进阶架构.md
28. 数据竞争与解决方案
题目
什么是数据竞争?有哪些解决方案?
深入解析
数据竞争:多线程同时访问同一内存,至少一次写入,导致结果不确定
解决方案:
| 方案 | 说明 | 问题 |
|---|---|---|
| 锁 | 临界区保护,同一时间只有一个线程访问 | 死锁、优先级反转 |
| 原子操作 | 硬件层面实现,无法同时被多个 CPU 执行 | 编程复杂 |
内存模型:不同 CPU 架构对指令重排序支持不同,ARM 比 x86 允许更多重排序
答题示例
数据竞争是多线程同时访问同一内存且至少一次写入,导致结果不确定。
锁提供临界区保护,但有死锁和优先级反转问题。原子操作无锁但编程复杂。
不同 CPU 内存模型不同,ARM 比 x86 允许更多重排序。
参考文章
- 20.面向数据编程与任务系统.md
29. Job System 架构
题目
现代游戏引擎的 Job System 是如何设计的?为什么用 Fiber 而不是线程?
深入解析
Fiber-based Job System:
- 线程是执行单元,纤程是上下文
- 每个处理器核心对应一个线程,最小化上下文切换
- 作业在纤程上下文中执行
为什么用 Fiber:
- 线程上下文切换需要进入内核,开销巨大(约 2000 CPU 周期 + 缓存失效)
- Fiber 切换在用户态完成,无需内核切换
- 一个线程可以在多个 Fiber 之间切换,避免等待
协程类型:
- 有栈协程:独立运行时栈,支持嵌套 yield,内存开销大
- 无栈协程:无独立栈,切换快但使用困难
答题示例
线程是执行单元,纤程是上下文。每个核心一个线程,作业在纤程中执行。
线程切换需进入内核开销大,Fiber 切换在用户态完成。
有栈协程支持嵌套 yield 但内存开销大,无栈协程切换快但使用困难。
参考文章
- 20.面向数据编程与任务系统.md
30. GPU 驱动渲染优势
题目
GPU 驱动渲染相比传统 CPU 驱动渲染有什么优势?
深入解析
传统渲染瓶颈:
- CPU 负载过高:剔除、LoD 选择、资源绑定
- GPU 空闲等待:CPU 供给跟不上
- DrawCall 碎片化:状态切换成本高
GPU 驱动渲染:
- Compute Shader 做 GPU 通用计算(剔除、LoD 选择)
- Indirect Draw 让 GPU 生成 draw 参数
- 从 DrawPrimitive 到 DrawScene
网格簇渲染:
- 几何拆分成固定大小簇(如 64/128 三角形)
- GPU 侧完成簇级剔除
- 索引缓冲压缩,只渲染可见三角形
答题示例
传统渲染 CPU 是瓶颈,GPU 空闲等待,DrawCall 碎片化。
GPU 驱动渲染用 Compute Shader 做剔除和 LoD 选择,Indirect Draw 让 GPU 生成参数。
网格簇渲染把几何拆分成固定簇,GPU 侧剔除,只渲染可见三角形。
参考文章
- 22.GPU驱动的几何管线Nanite.md
31. 全局光照实现方法
题目
游戏引擎如何实现全局光照?RSM 的原理是什么?
深入解析
全局光照 = 直接光照 + 间接光照(光线多次反弹)
RSM(反射式阴影贴图):
- 从光源视角渲染,被照亮的表面成为间接光源
- 每个 RSM 像素视为微小面光源
- 用锥体追踪随机采样而非遍历所有像素
蒙特卡洛积分:通过随机采样近似积分,采样越多噪声越小
重要性采样:在重要方向多采样,用更少样本获得更好结果
| 适用场景 | |
|---|---|
| 均匀分布 | 简单但效率低 |
| 余弦加权 | 漫反射表面 |
| GGX | 光滑表面 |
答题示例
全局光照 = 直接光照 + 间接光照。
RSM 从光源视角渲染,被照亮表面成为间接光源,用锥体追踪随机采样。
蒙特卡洛积分随机采样近似积分。重要性采样在重要方向多采样,余弦加权适合漫反射,GGX 适合光滑表面。
参考文章
- 21.动态全局光照和Lumen.md
深度题
1. 资源生命周期管理
题目
游戏引擎如何设计资源生命周期管理?如何平衡内存占用和加载效率?
深入解析
资源生命周期管理的核心是按需加载、及时释放、避免卡顿。
关键机制:
| 机制 | 作用 | 实现要点 |
|---|---|---|
| 延迟加载 | 按需加载资产 | 玩家”走到哪加载哪”,优先低精度再补高清 |
| 引用计数 + GC | 自动追踪使用情况 | 无引用时回收,避免内存泄漏 |
| 分帧回收 | 避免卡顿尖峰 | 单帧释放量限制,分多帧完成 |
| 预加载策略 | 提前加载可能需要的资源 | 根据场景预测玩家行为 |
| 资源池化 | 复用频繁创建销毁的资源 | 子弹、特效等使用对象池 |
平衡策略:
- 内存预算:为各类资源设置上限
- LOD 策略:距离远时用低精度资源
- 流式加载:后台异步加载,不阻塞主线程
答题示例
核心是按需加载、及时释放、避免卡顿。
关键机制:延迟加载按需加载,引用计数自动回收,分帧回收避免卡顿,预加载提前准备,资源池化复用。
平衡策略:内存预算、LOD 策略、流式加载。
参考文章
- 2.引擎架构分层.md
2. 热重载资源系统设计
题目
设计一个支持热重载的资源系统需要考虑哪些问题?
深入解析
热重载指运行时修改资源后立即生效,无需重启游戏。
核心问题:
| 问题 | 挑战 | 解决思路 |
|---|---|---|
| 依赖追踪 | 资源 A 依赖资源 B,B 修改后需通知 A | 维护依赖图,变更时级联通知 |
| 状态保持 | 重载后如何恢复运行时状态 | 序列化关键状态,重载后恢复 |
| 引用更新 | GUID 稳定但内存指针需更新 | Handle 间接访问,重载时更新映射 |
| 版本兼容 | 资源格式变更时处理旧数据 | 版本号 + 迁移脚本 |
| 多线程安全 | 渲染线程使用时逻辑线程修改 | 双缓冲或读写锁 |
答题示例
热重载是运行时修改资源后立即生效,无需重启。
核心问题:依赖追踪需维护依赖图,状态保持需序列化关键状态,引用更新用 Handle 间接访问,版本兼容用版本号迁移,多线程安全用双缓冲或读写锁。
参考文章
- 2.引擎架构分层.md
3. Shadow Map 原理与问题
题目
Shadow Map 的原理是什么?有哪些常见问题及解决方案?
深入解析
原理:从光源视角渲染深度图,渲染场景时将像素变换到光源空间,比较深度值判断是否在阴影中。
问题与解决:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 自遮挡(Shadow Acne) | 深度精度不足导致条纹 | 添加偏移(Bias),但过大会导致 Peter Panning |
| 锯齿 | 深度图分辨率不足 | PCF 滤波、级联阴影(CSM) |
| 大场景精度 | 单一阴影贴图覆盖范围有限 | 级联阴影(CSM),将视锥体分割为多个子视锥体 |
| 软阴影 | 硬阴影不真实 | PCSS(根据遮挡关系计算半影宽度)、VSSM(利用方差近似) |
答题示例
原理:从光源视角渲染深度图,渲染时比较深度值判断是否在阴影中。
自遮挡用 Bias 解决但注意 Peter Panning,锯齿用 PCF 或 CSM,大场景用级联阴影,软阴影用 PCSS 或 VSSM。
参考文章
- 5.游戏渲染中光和材质的数学魔法.md
4. Frame Graph 架构
题目
Frame Graph 是什么?为什么现代引擎需要它?
深入解析
Frame Graph 将渲染过程表示为有向无环图(DAG),节点是渲染 Pass,边是资源依赖关系。
解决的问题:
- 资源管理复杂:手动管理各种 Buffer 的创建、销毁、复用容易出错
- 依赖关系混乱:Pass 之间的执行顺序和资源依赖难以追踪
- 现代 API 复杂:Vulkan/DX12 暴露底层细节,手动管理易崩溃
Frame Graph 优势:
- 自动推导执行顺序
- 自动管理资源生命周期和复用(Aliasing)
- 声明式编程,降低错误率
- 便于可视化和调试
答题示例
Frame Graph 将渲染过程表示为有向无环图,节点是 Pass,边是资源依赖。
解决资源管理复杂、依赖关系混乱、现代 API 复杂的问题。
优势:自动推导执行顺序,自动管理资源生命周期,声明式编程降低错误率。
参考文章
- 7.游戏中渲染管线和后处理及其他.md
5. 角色动画系统设计
题目
如何设计一个支持复杂动画状态的角色动画系统?
深入解析
分层架构:
| 层级 | 职责 | 关键组件 |
|---|---|---|
| 动画片段层 | 基础动画资源 | Idle、Walk、Run、Jump 等 |
| 混合层 | 动画组合 | 线性插值、混合空间、骨骼遮罩、加法混合 |
| 状态机层(ASM) | 状态化动画管理 | 状态节点 + 转换条件 |
| 动画树层 | 复杂组合 | 表达式树组织上述组件 |
| IK 层 | 环境约束 | 动态调整姿态 |
关键设计:
- 控制参数系统:通过命名变量驱动混合权重
- 分层状态机:全身、上半身、下半身独立状态机
- 动画通知:在特定帧触发事件(如脚步声、攻击判定)
- 动画缓存:避免重复计算
答题示例
分层架构:动画片段层提供基础资源,混合层组合动画,状态机层管理状态化动画,动画树层组织复杂组合,IK 层处理环境约束。
关键设计:控制参数驱动权重,分层状态机独立管理身体部位,动画通知触发事件,动画缓存避免重复计算。
参考文章
- 9.动画树IK和表情动画.md
6. 动画混合滑步问题
题目
如何解决动画混合中的滑步问题?
深入解析
滑步(Ice Skating)是角色脚部在地面上滑动的现象。
原因:
- 时间轴未对齐:行走和奔跑步频不同,插值时脚落地时间错位
- 速度与权重关系不合理:插值后的移动速度与动画播放速度不匹配
解决方案:
| 方案 | 原理 | 适用场景 |
|---|---|---|
| 时间轴对齐 | 循环动画保证步频一致、脚落地时间对齐 | 预制作阶段 |
| 移动速度同步 | 插值后速度 = 各动画速度的加权平均 | 运行时计算 |
| 脚部 IK 锁定 | 脚落地时用 IK 固定脚部位置 | 运行时修正 |
| Turn In Place | 转向时脚部不滑动 | 原地转向场景 |
答题示例
滑步是角色脚部在地面上滑动,原因有时间轴未对齐、速度权重不合理。
解决:时间轴对齐保证步频一致,移动速度同步用加权平均,脚部 IK 锁定固定位置,Turn In Place 处理原地转向。
参考文章
- 9.动画树IK和表情动画.md
7. 动画重定向难点
题目
动画重定向是什么?有哪些难点?
深入解析
动画重定向是将一个角色的动画迁移到另一个角色,实现动画复用。
难点:
| 难点 | 问题 | 解决思路 |
|---|---|---|
| 骨骼比例差异 | 腿长不同导致脚部悬空或穿模 | IK 修正脚部位置 |
| 骨骼层级不同 | 源角色单脊柱,目标角色三脊柱 | 中间层映射 |
| 绑定姿势差异 | 关节朝向不一致 | 旋转补偿 |
| 自网格穿透 | 重定向后身体部位可能相交 | 手动微调或碰撞检测 |
答题示例
动画重定向是将一个角色的动画迁移到另一个角色,实现动画复用。
难点:骨骼比例差异用 IK 修正,骨骼层级不同用中间层映射,绑定姿势差异用旋转补偿,自网格穿透需手动微调。
参考文章
- 9.动画树IK和表情动画.md
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com