关于 ue-mass-entity
面向 UE 5.8 开发者的 Mass Entity 参考技能,讲解用于大规模 Agent 模拟的 ECS 数据、处理器和查询。内容覆盖实体生命周期、并行迭代及游戏玩法扩展模块。
主要功能
- 01Fragment、Tag 与 Archetype
- 02实体管理器与实体构建
- 03Processor 和查询配置
- 04分块迭代、并行处理与限流
- 05迭代期间的延迟结构变更
适用场景
- 01构建大量 Agent 的模拟逻辑
- 02编写按 Fragment 筛选实体的处理器
- 03在迭代中安全地添加或移除 Tag
- 04配置分块并行处理或跨帧限流
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ quodsoler/ unreal-engine-skills --skill ue-mass-entity安装后在 Claude Code 中输入/ue-mass-entity确认已加载。
附带文件references/mass-entity-patterns.mdreferences/mass-fragment-reference.md
详细介绍
UE Mass Entity
目标引擎:UE 5.8。以下 API 均已根据 5.8 头文件验证;旧版形式列在“已弃用 — 请勿使用”部分。
Mass 是一种基于原型的实体组件系统,使用缓存友好的数据布局和多线程处理器模拟数千个 Agent。核心位于始终可用的运行时模块 MassEntity 和 MassCore(以及 MassSignals、MassEngine);面向游戏的部分(生成、表示、移动、LOD、复制、Actor 桥接)来自 MassGameplay(在 5.8 中为 Experimental) 插件,导航和行为则来自 MassAI(在 5.8 中为 Experimental) 和 MassCrowd(在 5.8 中为 Experimental)。
上下文
如果 .agents/ue-project-context.md 存在,请阅读它(模块名称、约定、已启用的插件、GAS/网络设置)。如果该文件不存在,也请继续。
根据请求和代码库确定对应领域。仅当两种合理解读会导致不同代码时才提问。
| 请求涉及…… | 请参阅 |
|---|---|
| Build.cs、包含项、“启用插件” | 模块和 Build.cs |
| 声明片段、标签、共享数据/块数据 | 片段、标签和原型 |
| 创建/销毁实体、直接修改 | FMassEntityManager |
| 编写处理器、调度、阶段 | UMassProcessor |
| 选择实体、需求、筛选 | FMassEntityQuery |
| 在循环中读取/写入片段数据 | 迭代和 FMassExecutionContext |
| 迭代期间添加/移除片段 | 延迟命令 |
| 对片段/标签的添加或移除作出响应 | UMassObserverProcessor |
| 在处理器外访问单个实体、流畅构建 | 单实体访问和 FEntityBuilder |
| 父子链接、原型分组 | 关系和原型组 |
Traits、配置资源、AMassSpawner | Traits、配置资源和生成 |
| ISM 渲染、LOD、提升为 Actor | 表示和 LOD |
| 人群、车道、实体 AI | 人群、导航和行为 |
更完整的模板见 mass-entity-patterns.md;内置类型和字段见 mass-fragment-reference.md。
模块和 Build.cs
无需启用 MassEntity 插件。MassEntity 和 MassCore 是随引擎提供的运行时模块——将它们添加到 Build.cs 后即可链接。只有游戏功能层属于插件,并且必须在 .uproject 中启用。
PublicDependencyModuleNames.AddRange(new string[] {
"Core", "CoreUObject", "Engine",
"MassEntity", // manager, processors, queries, commands, relations
"MassCore", // FMassFragment/FMassTag bases, FTransformFragment, traits
"MassSignals", // UMassSignalSubsystem, UMassSignalProcessorBase
"MassCommon", // UE::Mass::ProcessorGroupNames (MassGameplay)
"MassSpawner", // UMassEntityTraitBase, AMassSpawner (MassGameplay)
"MassMovement", // FMassVelocityFragment (MassGameplay)
"MassRepresentation", // ISM visualization + LOD (MassGameplay)
"MassActors", // UMassAgentComponent, actor bridging (MassGameplay)
"MassNavigation", // FMassMoveTargetFragment (MassAI)
});
已移入 MassCore 的包含路径现在以 Mass/ 为命名空间:Mass/EntityElementTypes.h、Mass/EntityFragments.h、Mass/EntityHandle.h、Mass/ExternalSubsystemTraits.h、Mass/ArchetypeGroup.h。MassEntityTypes.h 仍会传递引入元素基类;建议使用显式的 Mass/ 路径。
片段、标签与原型
| 概念 | 基类 / 类型 | 用途 |
|---|---|---|
| 实体 | FMassEntityHandle | 由索引 + 序列号构成的身份句柄 |
| 片段 | FMassFragment | 每个实体的可变数据 |
| 稀疏片段 | FMassSparseFragment | 存储在原型之外的每个实体数据(添加时实体不会移动) |
| 标签 | FMassTag | 用于筛选的零大小标记 |
| 稀疏标签 | FMassSparseTag | 无需更改原型即可添加或移除的标记 |
| 块片段 | FMassChunkFragment | 每个内存块的一个实例 |
| 共享片段 | FMassSharedFragment | 引用它的实体共享的可变值 |
| 常量共享片段 | FMassConstSharedFragment | 不可变的共享配置 |
| 原型 | FMassArchetypeHandle | 唯一的片段 / 标签组合 |
以上类型均派生自 FMassElement,并在 Mass/EntityElementTypes.h 中声明。
// MyMassTypes.h
#pragma once
#include "Mass/EntityElementTypes.h"
#include "MyMassTypes.generated.h"
USTRUCT()
struct FMyHealthFragment : public FMassFragment
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, Category = "Mass")
float Current = 100.f;
UPROPERTY(EditAnywhere, Category = "Mass")
float Max = 100.f;
};
USTRUCT()
struct FMyAliveTag : public FMassTag { GENERATED_BODY() };
USTRUCT()
struct FMyDeadTag : public FMassTag { GENERATED_BODY() };
USTRUCT()
struct FMySquadSharedFragment : public FMassSharedFragment
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, Category = "Mass")
int32 SquadID = 0;
};
组成相同的实体共享一个原型,每种片段类型都按块连续存储。添加或移除片段或标签会将实体移至另一个原型——这就是结构性更改会延迟到迭代结束后执行的原因。稀疏片段和稀疏标签正是为避免此类移动而存在的。
FMassEntityManager
FMassEntityManager 不是 UObject;它是一个派生自 TSharedFromThis<FMassEntityManager> 和 FGCObject 的结构体。可通过 UMassEntitySubsystem(一个 UMassSubsystemBase,其自身又是 UWorldSubsystem)或 UE::Mass::Utils 访问它。
#include "MassEntitySubsystem.h"
#include "MassEntityManager.h"
UMassEntitySubsystem* MassSubsystem = GetWorld()->GetSubsystem<UMassEntitySubsystem>();
FMassEntityManager& EntityManager = MassSubsystem->GetMutableEntityManager();
// const access: MassSubsystem->GetEntityManager()
// free function: UE::Mass::Utils::GetEntityManagerChecked(*GetWorld())
FMassEntityHandle Entity = EntityManager.CreateEntity(ArchetypeHandle);
FMassEntityHandle Reserved = EntityManager.ReserveEntity();
EntityManager.BuildEntity(Reserved, ArchetypeHandle);
// Batch creation: hold the returned context until observers should fire.
TArray<FMassEntityHandle> Entities;
TSharedRef<FMassEntityManager::FEntityCreationContext> CreationContext =
EntityManager.BatchCreateEntities(ArchetypeHandle, 5000, Entities);
EntityManager.DestroyEntity(Entity);
EntityManager.BatchDestroyEntities(Entities);
FMassEntityHandle::IsSet()(及其别名 IsValid())只会报告索引和序列号是否非零。要查询实体是否确实存在,请使用管理器:
EntityManager.IsEntityValid(Handle); // handle refers to a live entity
EntityManager.IsEntityBuilt(Handle); // reserved handle has been built
EntityManager.IsEntityActive(Handle); // valid and built
在处理器迭代之外,可以直接进行结构性修改。添加 / 标签 / 交换调用接受 TNotNull<const UScriptStruct*>(RemoveFragmentFromEntity 仍接受普通的 const UScriptStruct*,MassEntityManager.h:430),因此请传入 T::StaticStruct(),绝不要传入可能为空的指针。
EntityManager.AddFragmentToEntity(Handle, FMyHealthFragment::StaticStruct());
EntityManager.RemoveFragmentFromEntity(Handle, FMyHealthFragment::StaticStruct());
EntityManager.AddTagToEntity(Handle, FMyDeadTag::StaticStruct());
EntityManager.RemoveTagFromEntity(Handle, FMyDeadTag::StaticStruct());
EntityManager.SwapTagsForEntity(Handle, FMyAliveTag::StaticStruct(), FMyDeadTag::StaticStruct());
实体存储支持并发:FMassEntityManager::Initialize(const FMassEntityManagerStorageInitParams&) 接受 FMassEntityManager_InitParams_Concurrent(MaxEntityCount、MaxEntitiesPerPage),并构建 FConcurrentEntityStorage。子系统会为你完成此操作;只有独立管理器需要调用它。
UMassProcessor
继承 UMassProcessor,使用 *this 构建查询,以便注册自身,并严格按照声明覆盖两个虚函数。
// MyMovementProcessor.h
#pragma once
#include "MassProcessor.h"
#include "MassEntityQuery.h"
#include "MyMovementProcessor.generated.h"
UCLASS()
class MYGAME_API UMyMovementProcessor : public UMassProcessor
{
GENERATED_BODY()
public:
UMyMovementProcessor();
protected:
virtual void ConfigureQueries(const TSharedRef<FMassEntityManager>& EntityManager) override;
virtual void Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) override;
private:
FMassEntityQuery MovementQuery;
};
// MyMovementProcessor.cpp
#include "MyMovementProcessor.h"
#include "MassCommonTypes.h" // UE::Mass::ProcessorGroupNames
#include "MassExecutionContext.h"
#include "MassMovementFragments.h" // FMassVelocityFragment, FMassMovementParameters
#include "MassSignalSubsystem.h"
#include "Mass/EntityFragments.h" // FTransformFragment
#include "MyMassTypes.h" // FMyHealthFragment, FMyDeadTag, FMySquadSharedFragment
UMyMovementProcessor::UMyMovementProcessor()
: MovementQuery(*this)
{
ProcessingPhase = EMassProcessingPhase::PrePhysics;
ExecutionFlags = static_cast<int32>(EProcessorExecutionFlags::AllNetModes);
ExecutionOrder.ExecuteInGroup = UE::Mass::ProcessorGroupNames::Movement;
ExecutionOrder.ExecuteAfter.Add(TEXT("MassApplyMovementProcessor")); // class FName: no U prefix
bRequiresGameThreadExecution = false;
}
EMassProcessingPhase:PrePhysics、StartPhysics、DuringPhysics、EndPhysics、PostPhysics、FrameEnd、MAX。
EProcessorExecutionFlags:None、Standalone、Server、Client、Editor、EditorWorld、AllNetModes(Standalone|Server|Client)、AllWorldModes(AllNetModes|EditorWorld)、All。
FMassProcessorExecutionOrder 包含 ExecuteInGroup(一个 FName),以及由处理器或组名称构成的 ExecuteBefore 和 ExecuteAfter 数组。处理器的名称是其类的 FName,不带 U 前缀(MassProcessorDependencySolver.cpp:560)——建议使用 UOther::StaticClass()->GetFName();拼写错误的名称只会变成一个虚拟节点,并输出一条 Log 级别的消息(:705),因此排序会悄然失效。命名组来自 UE::Mass::ProcessorGroupNames:UpdateWorldFromMass、SyncWorldToMass、Behavior、Tasks、Avoidance、ApplyForces、Movement。
FMassEntityQuery
在 ConfigureQueries 中声明需求,绝不要在构造函数中声明,因为查询会在 ConfigureQueries 运行前与实体管理器绑定。通过 FMassEntityQuery(UMassProcessor& Owner) 构建的查询已完成注册;默认构造的成员仍需调用 RegisterQuery(Query)。
void UMyMovementProcessor::ConfigureQueries(const TSharedRef<FMassEntityManager>& EntityManager)
{
MovementQuery.AddRequirement<FTransformFragment>(
EMassFragmentAccess::ReadWrite, EMassFragmentPresence::All);
MovementQuery.AddRequirement<FMassVelocityFragment>(
EMassFragmentAccess::ReadOnly, EMassFragmentPresence::All);
MovementQuery.AddRequirement<FMyHealthFragment>(
EMassFragmentAccess::ReadOnly, EMassFragmentPresence::Optional);
MovementQuery.AddTagRequirement<FMyDeadTag>(EMassFragmentPresence::None);
MovementQuery.AddSharedRequirement<FMySquadSharedFragment>(
EMassFragmentAccess::ReadOnly, EMassFragmentPresence::All);
MovementQuery.AddConstSharedRequirement<FMassMovementParameters>(
EMassFragmentPresence::All);
MovementQuery.AddSubsystemRequirement<UMassSignalSubsystem>(
EMassFragmentAccess::ReadWrite);
}
EMassFragmentAccess | 含义 |
|---|---|
None | 仅筛选,不绑定数据 |
ReadOnly | 通过 GetFragmentView<T>()(TConstArrayView)读取 |
ReadWrite | 通过 GetMutableFragmentView<T>()(TArrayView)读写 |
EMassFragmentPresence | 含义 |
|---|---|
All | 必须存在列出的每个元素 |
Any | 至少必须存在一个标记为 Any 的元素 |
None | 元素必须不存在 |
Optional | 存在时绑定,不存在时跳过 |
AddChunkRequirement<T>(Access, Presence) 会绑定一个 FMassChunkFragment。SetChunkFilter(const FMassChunkConditionFunction&) 接收一个 TFunction<bool(const FMassExecutionContext&)>,每个块只会对其求值一次,因此谓词可以读取块片段、共享片段和常量数据,但不能读取逐实体片段。
迭代和 FMassExecutionContext
void UMyMovementProcessor::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context)
{
MovementQuery.ForEachEntityChunk(Context, [](FMassExecutionContext& Context)
{
const int32 NumEntities = Context.GetNumEntities();
const TArrayView<FTransformFragment> Transforms = Context.GetMutableFragmentView<FTransformFragment>();
const TConstArrayView<FMassVelocityFragment> Velocities = Context.GetFragmentView<FMassVelocityFragment>();
const float DeltaTime = Context.GetDeltaTimeSeconds();
for (int32 Index = 0; Index < NumEntities; ++Index)
{
Transforms[Index].GetMutableTransform().AddToTranslation(Velocities[Index].Value * DeltaTime);
}
});
}
Lambda 仅接收 FMassExecutionContext&;没有实体管理器参数。其他访问器:GetEntities()(TConstArrayView<FMassEntityHandle>)、GetFragmentView<T>()、GetMutableFragmentView<T>()、GetChunkFragment<T>()、GetConstSharedFragment<T>()、GetMutableSharedFragment<T>()、GetSubsystem<T>()、GetMutableSubsystem<T>() 和 Defer()。
并行: ParallelForEachEntityChunk(Context, Lambda, FMassEntityQuery::EParallelExecutionFlags::Default);嵌套枚举的标志包括 Default、Force 和 AutoBalance(MassEntityQuery.h:67)。
分时处理: ForEachEntityChunk(Context, Limiter, Lambda) 接收一个由实体数量构造的 UE::Mass::FExecutionLimiter&,在访问指定数量的实体后停止(始终完成当前块),并在下次调用时从已存储的位置继续。将 limiter 作为处理器成员跨帧保留——新建的 limiter 会从头开始,而且 limiter 不会察觉两次调用之间创建或销毁的实体。
延迟命令
在 ForEachEntityChunk 内,绝不要通过实体管理器更改实体的组成:实体会移动到其他原型,当前持有的视图也会失效。应通过 Context.Defer() 将更改排入队列。
MovementQuery.ForEachEntityChunk(Context, [](FMassExecutionContext& Context)
{
const TConstArrayView<FMassEntityHandle> Entities = Context.GetEntities();
const TConstArrayView<FMyHealthFragment> Healths = Context.GetFragmentView<FMyHealthFragment>();
for (int32 Index = 0; Index < Context.GetNumEntities(); ++Index)
{
if (Healths[Index].Current <= 0.f)
{
Context.Defer().SwapTags<FMyAliveTag, FMyDeadTag>(Entities[Index]);
Context.Defer().RemoveFragment<FMyHealthFragment>(Entities[Index]);
}
}
});
FMassCommandBuffer 上的便捷调用:AddFragment<T>、RemoveFragment<T>、AddTag<T>、RemoveTag<T>、SwapTags<TOld, TNew>、AddElements<T...>、RemoveElements<T...>、DestroyEntity、DestroyEntities。添加和移除辅助函数会转发到 FMassCommandAddElements<T...> / FMassCommandRemoveElements<T...>,后者可在单次实体移动中处理任意组合的片段和标签(SwapTags 使用 FMassCommandSwapTagsInternal,MassCommandBuffer.h:264)。
若要添加片段并设置其值,直接推入命令类型:
Context.Defer().PushCommand<FMassCommandAddFragmentInstances<FMyHealthFragment>>(
Entities[Index], FMyHealthFragment{});
若要执行任意延迟工作,可通过 FMassDeferredCommand<OpType> 推入 Lambda:Context.Defer().PushCommand<FMassDeferredSetCommand>([Entity](FMassEntityManager& Manager) { ... });(别名包括 FMassDeferredCreate/Add/Remove/ChangeComposition/Set/DestroyCommand,MassCommands.h:1686-1744)。若要使用可复用的类型化命令,可继承 FMassBatchedCommand,重写 Run(FMassEntityManager&)(const Execute 已标记为 UE_DEPRECATED(5.7),MassCommands.h:202),并调用 PushUniqueCommand(TUniquePtr<FMassBatchedCommand>&&)。
命令按 EMassCommandOperationType 分桶刷新,而不是按枚举顺序:Create → Add → ChangeComposition → Set → Remove 和 Destroy(共享一个分桶,按推入顺序执行)→ 最后处理未分类的 None(MassCommandBuffer.cpp:110-119)。此顺序可确保实体在被修改前已经存在,片段在写入值之前已经存在。
UMassObserverProcessor
观察器会在被观察的片段或标签添加或移除时运行。填写 ObservedTypes(一个数组——单个观察器可以监视多个类型)和 ObservedOperations。
// MyHealthObserver.cpp
UMyHealthObserver::UMyHealthObserver()
: ObserverQuery(*this)
{
ObservedTypes.Add(FMyHealthFragment::StaticStruct());
ObservedOperations = EMassObservedOperationFlags::Add;
}
EMassObservedOperationFlags:None、AddElement、RemoveElement、CreateEntity、DestroyEntity,以及组合标志 Add(=AddElement | CreateEntity)、Remove(=RemoveElement | DestroyEntity)和 All。除非你有意忽略实体创建,否则请使用 Add/Remove——实体创建时就带有片段的情况下,单独使用 AddElement 不会触发。
重写 Register() 可以自定义注册的类型,并使用 GetObservedTypes() 或 GetSingleObservedTypeChecked() 读取这些类型。完整的观察器模板见 mass-entity-patterns.md。
单实体访问与 FEntityBuilder
FMassEntityView 提供对单个实体的类型化访问。它是临时对象——原型内存会发生重定位,因此每次都要构建新的视图,绝不要缓存视图。
if (EntityManager.IsEntityValid(Handle))
{
FMassEntityView View(EntityManager, Handle);
if (FMyHealthFragment* Health = View.GetFragmentDataPtr<FMyHealthFragment>())
{
Health->Current -= Damage;
}
const bool bDead = View.HasTag<FMyDeadTag>();
}
UE::Mass::FEntityBuilder(MassEntityBuilder.h)以流畅的方式组合实体并一次性提交,比反复调用 AddFragmentToEntity 更高效。
#include "MassEntityBuilder.h"
UE::Mass::FEntityBuilder Builder(EntityManager);
Builder.Add<FTransformFragment>(SpawnTransform);
Builder.Add<FMyHealthFragment>();
Builder.Add<FMyAliveTag>();
FMyHealthFragment& Health = Builder.GetOrCreate<FMyHealthFragment>();
Health.Current = 50.f;
const FMassEntityHandle NewEntity = Builder.Commit();
SetForceDeferredCommit(true) 会通过命令缓冲区提交;CommitAndReprepare() 可以复用构建器来处理下一个实体。
关系与原型组
关系用于表示实体之间的类型化链接。关系类型派生自 FMassRelation(其本身也是 FMassTag);引擎在 Relations/MassChildOf.h 中提供了 FMassChildOfRelation 和 FMassChildOfFragment。通过 UE::Mass::FRelationManager 创建和查询关系实例,可使用 EntityManager.GetRelationManager() 获取它。
#include "MassRelationManager.h"
#include "Relations/MassChildOf.h"
UE::Mass::FRelationManager& Relations = EntityManager.GetRelationManager();
Relations.CreateRelationInstance<FMassChildOfRelation>(ChildEntity, ParentEntity);
TArray<FMassEntityHandle> Children = Relations.GetRelationSubjects(
FMassChildOfRelation::StaticStruct(), ParentEntity);
延迟执行的等效写法:Context.Defer().PushCommand<FMassCommandMakeRelation<FMassChildOfRelation>>(ChildEntity, ParentEntity)。构建器支持 AddRelation<T>(OtherEntity)。
原型组可以让查询按受控顺序处理原型。EntityManager.FindOrAddArchetypeGroupType(GroupName) 返回一个 UE::Mass::FArchetypeGroupType;EntityManager.BatchGroupEntities(UE::Mass::FArchetypeGroupHandle(GroupType, UE::Mass::FArchetypeGroupID(N)), Entities) 会为实体分配分组(Mass/ArchetypeGroup.h:100);Query.GroupBy(GroupType)(可选地使用排序谓词)会让迭代按组进行。
特征、配置资产与生成
UMassEntityConfigAsset(一个 UDataAsset)保存一组特征,用于构建实体模板。特征继承 UMassEntityTraitBase 并实现 BuildTemplate;两个虚函数都是 const,且 ValidateTemplate 接受三个参数。
// MyHealthTrait.h
#pragma once
#include "MassEntityTraitBase.h"
#include "MyHealthTrait.generated.h"
UCLASS(meta = (DisplayName = "My Health"))
class MYGAME_API UMyHealthTrait : public UMassEntityTraitBase
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, Category = "Health")
float DefaultHealth = 100.f;
protected:
virtual void BuildTemplate(FMassEntityTemplateBuildContext& BuildContext,
const UWorld& World) const override;
virtual bool ValidateTemplate(const FMassEntityTemplateBuildContext& BuildContext,
const UWorld& World,
FAdditionalTraitRequirements& OutTraitRequirements) const override;
};
FMassEntityTemplateBuildContext(声明于 MassEntityTemplateRegistry.h)提供 AddFragment<T>()、AddFragment_GetRef<T>()、AddTag<T>()、AddChunkFragment<T>()、AddSharedFragment(const FSharedStruct&)、AddConstSharedFragment(const FConstSharedStruct&) 和 AddTranslator<T>()。
UMassAssortedFragmentsTrait(MassAssortedFragmentsTrait.h)可以添加在编辑器中配置的片段和标签列表,无需编写任何 C++。AMassSpawner 通过 EntityTypes 引用配置资产,并驱动 Count、DoSpawning() 和 DoDespawning()。
表示与 LOD
UMassRepresentationSubsystem 会池化 Instanced Static Mesh 组件和生成的 Actor,使数千个实体无需各自对应一个 Actor 也能完成渲染。
EMassRepresentationType | 用途 |
|---|---|
HighResSpawnedActor | 完整 Actor,用于最近的实体 |
LowResSpawnedActor | 用于中距离的轻量 Actor |
SkinnedMeshInstance | 实例化蒙皮网格 |
StaticMeshInstance | ISM,用于大部分人群 |
None | 不渲染 |
EMassLOD::Type 为 High、Medium、Low、Off、Max,存储为 TEnumAsByte<EMassLOD::Type>;可见性由 EMassVisibility(CanBeSeen、CulledByFrustum、CulledByDistance、Max)表示。UMassMovableVisualizationTrait 和 UMassStationaryVisualizationTrait 用于配置网格、Actor 类和 LOD 距离。
查询会触及的所有内容,其线程安全性都通过 Mass/ExternalSubsystemTraits.h 中的 Trait 声明。TMassExternalSubsystemTraits 默认 GameThreadOnly = true;TMassSharedFragmentTraits 默认 GameThreadOnly = false。TMassFragmentTraits 没有线程标记,只有 AuthorAcceptsItsNotTriviallyCopyable = false(Mass/ExternalSubsystemTraits.h:57-66)。仅在默认值不适用时才进行特化:
template<>
struct TMassExternalSubsystemTraits<UMyMassSubsystem> final
{
enum
{
GameThreadOnly = false,
ThreadSafeWrite = false
};
};
人群、导航与行为
UMassCrowdSubsystem(MassCrowd,在 5.8 中为实验性功能)跟踪 ZoneGraph 车道占用、密度和等待位置。实体包含 FMassCrowdTag 和 FMassCrowdLaneTrackingFragment;UMassCrowdMemberTrait 会将它们关联起来。该子系统将 TMassExternalSubsystemTraits<UMassCrowdSubsystem> 特化为 GameThreadOnly = false,因此并行处理器可通过 AddSubsystemRequirement 声明对此子系统的依赖。
移动目标存储在 FMassMoveTargetFragment 中(MassNavigationFragments.h,模块 MassNavigation)。ZoneGraph 本身和 Smart Objects 的内容请参阅 ue-ai-navigation。
实体行为通过 State Trees 运行:UMassStateTreeTrait 会附加资源,UMassStateTreeProcessor 派生自 UMassSignalProcessorBase,仅对收到信号的实体进行 Tick;FMassStateTreeSharedFragment 是 const 共享片段。使用 UMassSignalSubsystem::SignalEntity(FName SignalName, FMassEntityHandle Entity) 向实体发送信号——信号名称在前。任务和求值器的编写请参阅 ue-state-trees。
已弃用 — 请勿使用
| 不要使用 | 在 5.8 中使用 | 来源 |
|---|---|---|
ObservedType = T::StaticStruct(); | ObservedTypes.Add(T::StaticStruct()); | MassObserverProcessor.h 中的 UE_DEPRECATED(5.8) |
Operation = EMassObservedOperation::AddElement; | ObservedOperations = EMassObservedOperationFlags::Add; | MassObserverProcessor.h 中的 UE_DEPRECATED(5.7) |
GetObservedTypeChecked() | GetSingleObservedTypeChecked() | MassObserverProcessor.h 中的 UE_DEPRECATED(5.8) |
virtual void ConfigureQueries() | ConfigureQueries(const TSharedRef<FMassEntityManager>&) | MassProcessor.h 中的 UE_DEPRECATED(5.6) |
virtual void Initialize(UObject&) | InitializeInternal(UObject&, const TSharedRef<FMassEntityManager>&) | MassProcessor.h 中的 UE_DEPRECATED(5.6) |
ForEachEntityChunk(EntityManager, Context, Fn) | ForEachEntityChunk(Context, Fn) | MassEntityQuery.h 中的 UE_DEPRECATED(5.6) |
ParallelForEachEntityChunk(EntityManager, …)、EParallelForMode | ParallelForEachEntityChunk(Context, Fn, EParallelExecutionFlags) | MassEntityQuery.h 中的 UE_DEPRECATED(5.6) |
FMassEntityQuery Query{ FMyFragment::StaticStruct() }; | FMassEntityQuery Query{*this}; + 在 ConfigureQueries 中声明需求 | MassEntityQuery.h 中的 UE_DEPRECATED(5.6) |
Defer().AddTag_RuntimeCheck<T>() 和其他 *_RuntimeCheck 调用 | Defer().AddTag<T>()、AddFragment<T>()、RemoveFragment<T>()、RemoveTag<T>()、SwapTags<A,B>() | MassCommandBuffer.h 中的 UE_DEPRECATED(5.8) |
FMassCommandAddTagsInternal、FMassCommandAddFragmentsInternal | FMassCommandAddElements<T...> | MassCommands.h 中的 UE_DEPRECATED(5.8) |
FMassCommandRemoveTagsInternal、FMassCommandRemoveFragmentsInternal | FMassCommandRemoveElements<T...> | MassCommands.h 中的 UE_DEPRECATED(5.8) |
#include "MassExternalSubsystemTraits.h"、"MassEntityElementTypes.h"、"MassEntityFragments.h"、"MassEntityHandle.h" | Mass/ExternalSubsystemTraits.h、Mass/EntityElementTypes.h、Mass/EntityFragments.h、Mass/EntityHandle.h | 各 shim 中的 UE_DEPRECATED_HEADER(5.8) |
FMassEntityManager::Initialize() | Initialize(FMassEntityManagerStorageInitParams),并使用 FMassEntityManager_InitParams_Concurrent | MassEntityManager.h 中的 UE_DEPRECATED(5.8) |
UMassRepresentationSubsystem::GetOrSpawnActorFromTemplate | GetOrRequestSpawnActorFromTemplate | MassRepresentationSubsystem.h:246 中的 UE_DEPRECATED(5.8) |
FMassLookAtTargetTag | FMassLookAtTargetFragment | MassLookAtFragments.h 中的 UE_DEPRECATED(5.6) |
常见错误
把 Mass 当作插件:MassEntity 和 MassCore 是运行时模块,没有 .uplugin。在 Build.cs 中添加 "MassEntity" 即可;只有 MassGameplay、MassAI 和 MassCrowd 需要在 .uproject 中启用。
**FTransformFragment 的头文件错误:**它位于 Mass/EntityFragments.h(模块 MassCore)中。MassCommonFragments.h 只是包含了它;MassCommon 和 MassEntity 将 MassCore 列为公共依赖,因此可以传递解析,但应包含 Mass/EntityFragments.h 并显式列出 MassCore,而不是依赖这种传递关系。
**在迭代期间进行结构性更改:**在 ForEachEntityChunk 中调用 EntityManager.AddFragmentToEntity(...) 会移动实体,并使所有正在使用的视图失效。请使用 Context.Defer().AddFragment<T>(Entity)。
**在构造函数中声明需求:**查询会在 ConfigureQueries 运行前不久绑定到其实体管理器,因此在构造函数中调用 AddRequirement 会触发“Modifying requirements before initialization”检查。只在 ConfigureQueries 中声明需求。
忘记注册默认构造的查询:FMassEntityQuery MyQuery; 在调用 RegisterQuery(MyQuery) 前不会匹配任何内容,且不会给出提示。建议在构造函数初始化列表中使用 MyQuery(*this)。
**缓存 FMassEntityView:**视图指向可能会移动的原型内存。每次使用时都应根据句柄重新构建视图。
**用 IsSet() 检查是否存在:**指向已销毁实体的句柄仍会报告 IsSet()/IsValid() 为 true。请调用 EntityManager.IsEntityValid(Handle)。
**访问方式不匹配:**请求 ReadOnly 后调用 GetMutableFragmentView<T>() 会触发断言。请确保视图与声明的 EMassFragmentAccess 匹配。
**只监听 AddElement:**创建时就已包含该片段的实体会触发 CreateEntity,而非 AddElement。请使用 EMassObservedOperationFlags::Add。
**从并行处理器访问 UObject:**设置 bRequiresGameThreadExecution = true,或者通过 AddSubsystemRequirement 声明子系统,并为其提供如实声明特性的 TMassExternalSubsystemTraits 特化。
相关技能
ue-state-trees— State Tree 资产、任务、评估器和 Mass State Tree 架构ue-ai-navigation— ZoneGraph、Smart Objects、NavMesh 和感知ue-async-threading— 任务图、ParallelFor和线程安全基础ue-actor-component-architecture—UMassAgentComponent另一侧的 Actor 和组件ue-cpp-foundations—USTRUCT/UCLASS反射、子系统、模块布局ue-animation-system— 将实体提升为 Actor 或蒙皮实例后的动画ue-procedural-generation— 为 Mass 生成提供数据的 PCG 和 ISM 编写ue-mover— Mover 插件:移动模式、分层移动和回滚网络