UE5.8 Mass Framework 专题系列

UE5.8 Mass 专题(十二):从零搭一个城市场景人群系统

用一个城市场景人群案例串起 Config、Spawner、StateTree、ZoneGraph、SmartObject、Representation、Replication、调试和上线建议。

总览

这一篇把前面的原理串成一个可执行方案:做一个城市场景人群系统。目标不是一次做出完整开放世界 NPC,而是先跑通 200 个路人从生成、移动、闲逛、使用设施、LOD 表现到调试的闭环,再逐步扩到上千实体、联网和工具化。

项目假设:玩家在一条商业街移动,街上有行人、人行道 ZoneGraph、咖啡机和长椅 SmartObject,近处路人用 Actor 或 Skinned 表现,远处用 ISM,服务器可选权威同步。

UE5.8 Mass 专题(十二):从零搭一个城市场景人群系统 配图
生产项目更需要路线图:先跑通 200 个实体的闭环,再逐步加入路网、交互、LOD、联网和工具化验收。

源码依据

这个案例会用到 MassEntity 的 Entity/Fragment/Query/Processor,MassSpawner 的 Trait/Config/Spawner,MassMovement 的 DesiredMovement 和 MovementParameters,MassAI 的 StateTree、ZoneGraph Navigation 和 SmartObject Task,MassRepresentation 的 LOD/Actor/ISM 切换,以及 MassReplication 的 Client Bubble。它不是一个单模块功能,而是 MassGameplay 和 MassAI 的典型组合应用。

架构分析

这个案例的核心不是“生成很多人”,而是建立一条可回退的数据链:Config 决定实体组成,Spawner 决定出现位置,StateTree 决定目标,ZoneGraph 和 Movement 推进移动,SmartObject 处理世界交互,Representation 控制显示预算,Replication 只同步相关切片。每一层都可以单独禁用和验证,避免一个问题把整条链拖进黑盒。

使用案例

MVP 阶段只实现商业街路人:从地铁口生成,沿人行道随机移动,看到咖啡机或长椅时尝试 Claim SmartObject,使用后继续闲逛,离玩家远时降级为 ISM 或 None。这个案例足够覆盖 Mass 的主要工作流,又不会被复杂剧情、任务系统或战斗系统干扰。

MVP 架构

资产/代码 验收
数据 FUrbanCrowdIntentFragmentFUrbanCrowdStatsFragment 能在 Debugger 看到字段
模板 DA_UrbanPedestrianConfig Validate Entity Config 通过
生成 AMassSpawner + ZoneGraph Spawn Points BeginPlay 生成 200 个实体
决策 ST_UrbanPedestrian Idle/Move/Use 三状态切换
移动 ZoneGraph + Movement Trait 实体沿人行道走,不乱穿墙
表现 Representation LOD 近处有人形,远处降级
调试 项目 Crowd Debug 面板 显示实体数、状态数、LOD 分布

实施步骤

第一周只做数据和移动。创建自定义 Trait,加入业务 Fragment;配置 Transform、Movement、ZoneGraph Navigation;在地图放 Spawner,让实体沿路网随机移动。此时不要接 SmartObject,不要做复杂表现,先验证“实体能生成、能移动、能销毁”。

第二周加入表现和 LOD。配置 Representation Trait,高 LOD 使用低成本 Actor 或 Skinned 表现,低 LOD 使用 StaticMeshInstance。调试镜头快速接近/远离人群,确认 Actor 请求、回收和 ISM 更新没有明显闪烁。

第三周加入行为。创建 Mass StateTree,Idle 状态等待随机时间,Move 状态选择 ZoneGraph 目标,Use 状态 Claim/Use SmartObject。所有外部交互都要有失败回退:Claim 失败就换目标,移动超时就释放 Claim,使用完成或取消都清理状态。

第四周做规模和联网。把数量从 200 提到 1000,记录 Processor 耗时和结构变更。需要联网时,只同步玩家附近 Bubble 内实体的 Transform 和 VisualState,不同步完整 StateTree。

代码演示

一个项目 Trait 可以把核心组合集中起来:

void UUrbanPedestrianTrait::BuildTemplate(FMassEntityTemplateBuildContext& BuildContext, const UWorld& World) const
{
    BuildContext.AddFragment<FUrbanCrowdIntentFragment>();
    BuildContext.AddFragment<FUrbanCrowdStatsFragment>();
    BuildContext.AddTag<FUrbanPedestrianTag>();

    FUrbanCrowdTuning Tuning;
    Tuning.MinIdleTime = MinIdleTime;
    Tuning.MaxIdleTime = MaxIdleTime;
    Tuning.SmartObjectCooldown = SmartObjectCooldown;
    BuildContext.AddConstSharedFragment(Tuning);
}

业务 Processor 不直接碰 Actor:

if (Intent.Mode == EUrbanCrowdMode::MoveToPoint)
{
    DesiredMovement.DesiredVelocity = (Intent.Target - Location).GetSafeNormal2D() * Tuning.WalkSpeed;
}

Actor 表现、动画和交互由 Representation 或近处桥接 Processor 处理。这样远处实体没有 Actor 时,行为仍然成立。

项目落地

内容规范比代码更重要。给每类人群一个 Config 命名规范,给 ZoneGraph Lane Tag 一个表,给 SmartObject Slot 定义容量和冷却,给 LOD 档位定义平台预算。每次新增一种人群,都必须通过同一套检查:Config Validate、生成数量、Processor Top、LOD 分布、SmartObject 泄漏、打包引用。

常见坑

不要一上来追求“真实城市”。先做少状态、少 Trait、少表现类型的闭环。不要把所有路人差异做成不同 Archetype,外观和参数可以通过共享配置或表现描述解决。不要让 SmartObject 失败路径空着,规模一大就会出现实体卡死。不要等最后才加调试面板,Mass 项目没有可观测性会非常难维护。

源码路径索引

  • Engine/Plugins/Runtime/MassGameplay/Source
  • Engine/Plugins/AI/MassAI/Source
  • Engine/Plugins/AI/MassCrowd/Source
  • Engine/Plugins/Runtime/MassGameplay/Source/MassReplication