总览
UMassProcessor 是 Mass 运行逻辑的基本单元,但它不是普通 Tick()。Processor 会声明 Query、执行阶段、执行顺序、线程要求和激活状态。Mass 的 Phase Manager 把多个 Processor 组织到 PrePhysics、DuringPhysics、PostPhysics 等阶段,Dependency Solver 再根据 Query 读写需求和显式顺序构建执行图。
这套系统的目标是让项目能写大量小 Processor,而不是一个巨大 Manager。小 Processor 更容易声明依赖、更容易并行,也更容易调试某个环节为什么没写出预期 Fragment。
源码依据
MassProcessor.h 中 UMassProcessor 拥有 FMassProcessorExecutionOrder,包含 ExecuteInGroup、ExecuteBefore、ExecuteAfter;默认 Phase 是 EMassProcessingPhase::PrePhysics。Processor 通过 ConfigureQueries 配置需求,通过 Execute 执行逻辑,通过 RegisterQuery 或 Query.RegisterWithProcessor(*this) 暴露需求。MassProcessorDependencySolver.h 负责依赖求解,MassProcessingPhaseManager.h 管阶段调度。
架构分析
调度有两种依赖:数据依赖和业务依赖。数据依赖来自 Query 读写声明,例如移动意图 Processor 写 FMassDesiredMovementFragment,应用移动 Processor 读它;业务依赖来自显式执行顺序,例如你希望“目的地选择”一定在“移动意图”之前,即使它们不直接写同一个 Fragment。好的 Mass 架构应该先靠数据依赖,再用少量业务依赖补充。
Processor 分组也很重要。Representation 相关源码里有 UE::Mass::ProcessorGroupNames::Representation,Movement、LOD、Replication 也都有各自模块边界。项目自定义 Processor 可以按 Project.Crowd.Decision、Project.Crowd.Movement、Project.Crowd.Visual 这样的组名组织,避免 ExecuteBefore/After 全都指向具体类,导致后期改名和替换困难。
使用案例
一个人群模拟的阶段可以这样排:
| 阶段/组 | Processor | 输入 | 输出 |
|---|---|---|---|
| PrePhysics.Decision | 选择目的地 | StateTree/Intent | TargetLocation |
| PrePhysics.Movement | 生成期望移动 | Transform/Target | DesiredMovement |
| PrePhysics.Movement | 避障和转向 | DesiredMovement/Obstacle | Force/DesiredMovement |
| PrePhysics.Apply | 应用移动 | DesiredMovement/Velocity | Transform/Velocity |
| PostPhysics.Visual | Representation | Transform/LOD | Actor/ISM 更新 |
示例构造函数:
UShopperDesiredMovementProcessor::UShopperDesiredMovementProcessor()
{
ExecutionOrder.ExecuteInGroup = UE::Mass::ProcessorGroupNames::Movement;
ExecutionOrder.ExecuteAfter.Add(TEXT("Project.Crowd.Decision"));
ExecutionFlags = static_cast<int32>(EProcessorExecutionFlags::All);
bAutoRegisterWithProcessingPhases = true;
}
实际项目里更推荐把自定义组名集中定义,避免字符串散落。
项目落地
先画数据流,再写 Processor。每个 Processor 名称尽量体现“写什么”:ChooseDestination、BuildDesiredMovement、ApplyCheckoutState 比 CrowdProcessor 更好。每个 Processor 的 Query 控制在 3 到 6 个关键 Requirement 以内;超过这个范围,通常说明它承担了多个职责。
常见坑
不要用 ExecuteBefore/After 修复错误的读写声明。不要让一个 Processor 同时做决策、移动、表现和销毁。不要默认 Processor 能并行,访问 World、Actor、Component、非线程安全 Subsystem 时要明确要求游戏线程。不要忽略 ActivationState,OneShot 或手动激活适合初始化和批处理工具,不适合长期模拟。
源码路径索引
MassEntity/Public/MassProcessor.hMassEntity/Public/MassProcessingPhaseManager.hMassEntity/Public/MassProcessorDependencySolver.hMassEntity/Public/MassProcessingTypes.h