Mass学习知识点
一、前置知识
1.1 必备基础
- C++ 现代特性(模板、CRTP、移动语义)
- UE5 基础(Actor/Component、Subsystem、GameplayTag)
- 数据结构基础(内存布局、缓存友好性概念)
1.2 有帮助的背景知识
- ECS 架构概念(Entity、Component、System 的关系)
- DOD(Data-Oriented Design)思想
- 多线程基础(竞争条件、读写锁)
二、Mass 核心架构
2.1 数据原语
- Fragment:数据存储单元,Plain Struct,无虚函数限制
- Tag:零大小标记,触发 Archetype 分类
- Chunk Fragment:Chunk 粒度共享数据
- Shared Fragment:跨 Archetype 的只读共享配置
2.2 实体组织
- Entity:轻量句柄(FMassEntityHandle),无实体数据
- Archetype:Fragment 组合的唯一类型,决定内存布局
- Chunk:Archetype 下的内存块,默认容纳 128 个实体
- Archetype 迁移机制:Tag/Fragment 变化时的数据搬运代价
2.3 逻辑单元
- Processor:继承 UMassProcessor,声明 Query + 实现 Execute
- ProcessorGroup:Processor 的分组、排序与依赖声明
- Processing Phase:帧内执行阶段(PrePhysics / Physics / PostPhysics / FrameEnd)
对应游戏逻辑来说,各 Phase 的职责:
-
PrePhysics — 最常用的逻辑阶段,大多数 Processor 放这里
- 输入处理、AI决策
- 子弹生成/销毁判断
- 速度/方向更新
- 生命周期计时(ElapsedTime += DeltaTime)
-
StartPhysics — 把数据“喂“给物理引擎之前
- 将 Mass 数据同步到 Chaos/PhysX(如果你用了物理 Actor 桥接)
- 设置物理 body 的速度、位置
-
DuringPhysics — 物理模拟并行运行期间,CPU 空闲时做的事
- 纯计算、无副作用的任务(动画预算、LOD计算)
- 不能读写物理结果,因为物理还没算完
- bullet hell 场景基本不用这个
-
EndPhysics — 物理结果已出来,可以读取
- 从物理引擎回读碰撞结果
- 处理碰撞响应(子弹击中判定、伤害计算)
- 销毁被标记的实体
-
PostPhysics — 所有逻辑和物理都结束后
- 同步 Transform 到渲染(ISM/Niagara buffer 更新)
- Debug Draw
- 统计数据收集
-
FrameEnd — 帧末尾清理
- 清理临时数据
- 重置帧标记 flag
自定义碰撞可以参考下面的processor的phase:
PrePhysics: BulletLifetimeProcessor, BulletMovementProcessor
EndPhysics: BulletHitProcessor (碰撞结果处理)
PostPhysics: BulletRenderSyncProcessor, BulletDebugProcessor
2.4 核心管理器
- FMassEntityManager:实体增删改查的核心 API
- UMassEntitySubsystem:全局入口,获取 EntityManager
- FMassExecutionContext:Processor 执行时的上下文,含延迟命令队列
三、Query 系统
3.1 Query 声明
FMassEntityQuery的构造与注册- 访问类型:
EMassFragmentAccess::ReadOnly / ReadWrite - 存在性过滤:
EMassFragmentPresence::All / Any / None / Optional
3.2 Query 遍历
ForEachEntityChunk:Chunk 级批量遍历(推荐)GetMutableFragmentView/GetFragmentView:获取 Fragment 数组视图GetEntityCollection:获取实体句柄列表
3.3 延迟命令
FMassExecutionContext::Defer():安全的帧内结构变更AddFragment/RemoveFragment/AddTag/RemoveTag的延迟版本- 为什么不能在遍历中直接修改结构(迭代器失效问题)
四、内存模型与性能
4.1 内存布局
- SoA(Struct of Arrays)vs AoS(Array of Structs)
- Fragment 在 Chunk 内的连续存储方式
- 缓存行(Cache Line)与批量处理的关系
4.2 性能关键点
- Archetype 数量控制:避免 Fragment 组合爆炸
- Tag 使用代价:每次 AddTag / RemoveTag 都触发实体迁移
- Chunk 利用率:实体数量与 Chunk 填充率的关系
- Processor 依赖图:减少不必要的同步点
4.3 多线程模型
- Processor 并行执行条件(无写冲突)
ExecutionFlags控制线程策略- 共享资源访问:Subsystem 的线程安全注意事项
五、实体生命周期管理
5.1 创建
BatchCreateEntities:批量创建推荐方式FMassEntityTemplate与FMassArchetypeHandle- 初始化 Fragment 数据的时机
5.2 修改
- 结构修改(Fragment / Tag 增删)vs 数据修改
FMassCommandBuffer:批量延迟命令的底层机制- 帧内修改 vs 跨帧修改
5.3 销毁
DestroyEntity/BatchDestroyEntities- 销毁时的内存回收策略(Chunk 空洞处理)
- 与 Actor 销毁的同步问题
六、与 UE 生态集成
6.1 Mass + Actor 桥接
FMassActorFragment:Entity 持有 Actor 引用UMassActorSubsystem:Actor 到 Entity 的反向查找- 生命周期同步:Actor Spawn / Destroy 与 Entity 的对应
6.2 Mass + Niagara
FNiagaraDataInterfaceMassEntity:将 Fragment 数据暴露给 GPU- Data Interface 的读写同步机制
- 大规模粒子渲染的典型架构(CPU 逻辑 + GPU 渲染分离)
6.3 Mass + StateTree
- StateTree 作为 Mass 的 AI 行为驱动
FMassStateTreeFragment的使用- 状态机与 ECS 的结合点
6.4 Mass Signal 系统
UMassSignalSubsystem:实体间事件通知- Signal 的发送与订阅机制
- 与传统 Delegate 的区别和适用场景
七、官方插件解析
7.1 MassGameplay 插件
UMassMovementProcessor:内置移动处理UMassLookAtProcessor:朝向计算- 常用内置 Fragment 速查
7.2 MassAI 插件
- ZoneGraph 与 Mass 的配合
- Mass Crowd 人群系统架构
- LOD 分级策略(高频 Processor → 低频 Processor → 纯数据)
7.3 MassTraffic 插件
- 交通模拟的 Mass 实现思路
- 可借鉴的大规模实体管理模式
八、调试与工具链
8.1 运行时调试
- Mass Debugger:Archetype 分布、实体状态查看
- Unreal Insights:Processor 执行时序分析
UE_MASS_EXECUTE_PROCESSOR_DEBUG相关宏
8.2 常见问题排查
- Entity 句柄失效(Handle Invalidation)
- Processor 执行顺序不符合预期
- Archetype 数量异常增长
- 多线程数据竞争定位
8.3 单元测试
- Mass 系统的可测试性设计
- 用
FMassEntityManager在非 World 环境下测试 Processor
九、设计模式与最佳实践
- 组合优于继承:Fragment 粒度设计原则
- Tag 状态机:用 Tag 变化驱动行为切换
- 批量操作优先:避免逐实体调用 API
- 读写分离:将只读 Processor 和写 Processor 分开以最大化并行
- 延迟销毁模式:用 Tag 标记 + 延迟批量销毁代替即时销毁
- 配置与数据分离:用 Shared Fragment 存配置,Fragment 存状态
十、知识掌握自测
每个阶段完成后可以自问:
| 阶段 | 自测问题 |
|---|---|
| 入门 | 能否独立定义 Fragment、写一个 Processor 让实体移动? |
| 进阶 | 能否解释 Archetype 迁移的代价,并设计出避免频繁迁移的方案? |
| 熟练 | 能否将 Mass 与 Niagara 集成,实现万级实体的高性能渲染? |
| 精通 | 能否分析一个 Mass 系统的性能瓶颈,并给出优化方案? |