ue-mass-entity
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseUE Mass Entity Framework
UE Mass Entity 框架
You are an expert in Unreal Engine's Mass Entity framework -- an archetype-based Entity Component System (ECS) designed for high-performance simulation of thousands of entities using cache-friendly data layouts and parallel processing.
你是Unreal Engine的Mass Entity框架专家——这是一个基于原型的实体组件系统(ECS),采用缓存友好的数据布局和并行处理,专为数千个实体的高性能模拟而设计。
Context Check
上下文检查
Before proceeding, read to determine:
.agents/ue-project-context.md- Whether the MassEntity plugin is enabled (and MassAI, MassCrowd, MassGameplay if needed)
- The target entity count and performance budget
- Whether MassCrowd lane navigation or ZoneGraph is in use
- Existing processors, fragments, traits, and entity config assets
在开始之前,请阅读以确定:
.agents/ue-project-context.md- 是否启用了MassEntity插件(若需要,还需确认MassAI、MassCrowd、MassGameplay是否启用)
- 目标实体数量和性能预算
- 是否在使用MassCrowd路径导航或ZoneGraph
- 现有的处理器、片段、特性和实体配置资源
Information Gathering
信息收集
Ask the developer:
- What kind of entities are being simulated? (crowds, projectiles, traffic, wildlife, custom)
- What data does each entity carry? (position, velocity, health, custom state)
- Are entities visualized? If so, what LOD strategy? (ISM, skeletal, actor promotion)
- Is this multiplayer? If so, which entities replicate?
- How many entities at peak? (hundreds vs. tens of thousands)
向开发者询问:
- 正在模拟哪种类型的实体?(人群、投射物、交通、野生动物、自定义)
- 每个实体携带哪些数据?(位置、速度、生命值、自定义状态)
- 实体是否需要可视化?如果是,采用何种LOD策略?(ISM、骨骼网格体、Actor升级)
- 是否为多人游戏?如果是,哪些实体需要同步?
- 峰值时的实体数量是多少?(数百个 vs 数万个)
ECS Concepts
ECS 核心概念
Mass Entity uses an archetype ECS model where entity composition determines memory layout:
| Concept | Class Base | Purpose |
|---|---|---|
| Entity | | 8-byte identity handle (Index + SerialNumber) |
| Fragment | | Per-entity mutable data (position, velocity, health) |
| Tag | | Zero-size boolean marker for filtering |
| Shared Fragment | | Per-archetype mutable data |
| Const Shared Fragment | | Per-archetype immutable data (mesh params) |
| Chunk Fragment | | Per-memory-chunk data (custom chunk-level state) |
| Archetype | | Unique combination of fragment/tag types |
Why archetypes matter: Entities with identical fragment/tag composition share the same archetype. All fragments of the same type within a chunk are stored contiguously, enabling cache-friendly iteration over thousands of entities per frame.
Mass Entity采用基于原型的ECS模型,实体的组成决定了内存布局:
| 概念 | 基类 | 用途 |
|---|---|---|
| Entity | | 8字节的身份句柄(索引+序列号) |
| Fragment | | 每个实体的可变数据(位置、速度、生命值) |
| Tag | | 用于过滤的零大小布尔标记 |
| Shared Fragment | | 每个原型的可变数据 |
| Const Shared Fragment | | 每个原型的不可变数据(网格参数) |
| Chunk Fragment | | 每个内存块的数据(自定义块级状态) |
| Archetype | | 片段/标签类型的唯一组合 |
原型的重要性:具有相同片段/标签组合的实体共享同一个原型。同一个块内的所有同类型片段会连续存储,从而实现每帧对数千个实体的缓存友好型迭代。
Fragment and Tag Definitions
片段与标签定义
All types require with :
USTRUCT()GENERATED_BODY()cpp
// Per-entity mutable data
USTRUCT()
struct FHealthFragment : public FMassFragment
{
GENERATED_BODY()
float Current = 100.f;
float Max = 100.f;
};
// Zero-size marker — no data members
USTRUCT()
struct FDeadTag : public FMassTag
{
GENERATED_BODY()
};
// Shared across all entities in an archetype (mutable)
USTRUCT()
struct FTeamSharedFragment : public FMassSharedFragment
{
GENERATED_BODY()
int32 TeamID = 0;
};Chunk fragments () store per-memory-chunk state shared across all entities in a chunk. Note: inherits from (per-entity), not . Const shared fragments () are immutable after archetype creation -- use for configuration data like . See for built-in types.
FMassChunkFragmentFMassRepresentationLODFragmentFMassFragmentFMassChunkFragmentFMassConstSharedFragmentFMassRepresentationParametersreferences/mass-fragment-reference.md所有类型都需要带有的:
GENERATED_BODY()USTRUCT()cpp
// 每个实体的可变数据
USTRUCT()
struct FHealthFragment : public FMassFragment
{
GENERATED_BODY()
float Current = 100.f;
float Max = 100.f;
};
// 零大小标记——无数据成员
USTRUCT()
struct FDeadTag : public FMassTag
{
GENERATED_BODY()
};
// 在一个原型的所有实体间共享(可变)
USTRUCT()
struct FTeamSharedFragment : public FMassSharedFragment
{
GENERATED_BODY()
int32 TeamID = 0;
};块片段()存储在一个内存块内所有实体共享的块级状态。注意:继承自(每个实体独有),而非。常量共享片段()在原型创建后不可变——可用于这类配置数据。如需内置类型,请查看。
FMassChunkFragmentFMassRepresentationLODFragmentFMassFragmentFMassChunkFragmentFMassConstSharedFragmentFMassRepresentationParametersreferences/mass-fragment-reference.mdFMassEntityManager
FMassEntityManager
The entity manager is NOT a -- it is a struct (, ). Access it through (a ):
UObjectTSharedFromThis<FMassEntityManager>FGCObjectUMassEntitySubsystemUWorldSubsystemcpp
UMassEntitySubsystem* MassSubsystem = GetWorld()->GetSubsystem<UMassEntitySubsystem>();
FMassEntityManager& EntityManager = MassSubsystem->GetMutableEntityManager();
// const ref: MassSubsystem->GetEntityManager()实体管理器不是——它是一个结构体(,)。可通过(一个)访问它:
UObjectTSharedFromThis<FMassEntityManager>FGCObjectUMassEntitySubsystemUWorldSubsystemcpp
UMassEntitySubsystem* MassSubsystem = GetWorld()->GetSubsystem<UMassEntitySubsystem>();
FMassEntityManager& EntityManager = MassSubsystem->GetMutableEntityManager();
// 常量引用: MassSubsystem->GetEntityManager()Entity Lifecycle
实体生命周期
cpp
// One-shot creation
FMassEntityHandle Entity = EntityManager.CreateEntity(ArchetypeHandle);
// With shared fragments
FMassArchetypeSharedFragmentValues SharedValues;
FMassEntityHandle Entity = EntityManager.CreateEntity(ArchetypeHandle, SharedValues);
// Two-phase (reserve then build)
FMassEntityHandle Handle = EntityManager.ReserveEntity();
EntityManager.BuildEntity(Handle, ArchetypeHandle);
// Batch creation (thousands at once)
// BatchCreateEntities returns TSharedRef<FEntityCreationContext> — retain it until
// observer processors should fire (dropping it early suppresses observer execution).
TArray<FMassEntityHandle> Entities;
TSharedRef<FEntityCreationContext> CreationContext =
EntityManager.BatchCreateEntities(ArchetypeHandle, 5000, Entities);
// Destruction
EntityManager.DestroyEntity(Handle);
EntityManager.BatchDestroyEntities(EntityArray);cpp
// 一次性创建
FMassEntityHandle Entity = EntityManager.CreateEntity(ArchetypeHandle);
// 带共享片段
FMassArchetypeSharedFragmentValues SharedValues;
FMassEntityHandle Entity = EntityManager.CreateEntity(ArchetypeHandle, SharedValues);
// 两阶段创建(先预留再构建)
FMassEntityHandle Handle = EntityManager.ReserveEntity();
EntityManager.BuildEntity(Handle, ArchetypeHandle);
// 批量创建(一次性数千个)
// BatchCreateEntities返回TSharedRef<FEntityCreationContext> —— 保留该对象直到观察者处理器应触发(提前释放会抑制观察者执行)。
TArray<FMassEntityHandle> Entities;
TSharedRef<FEntityCreationContext> CreationContext =
EntityManager.BatchCreateEntities(ArchetypeHandle, 5000, Entities);
// 销毁
EntityManager.DestroyEntity(Handle);
EntityManager.BatchDestroyEntities(EntityArray);Validity Checks
有效性检查
FMassEntityHandle::IsSet()IsValid()cpp
EntityManager.IsEntityValid(Handle) // entity exists
EntityManager.IsEntityBuilt(Handle) // fully constructed
EntityManager.IsEntityActive(Handle) // active in simulationFMassEntityHandle::IsSet()IsValid()cpp
EntityManager.IsEntityValid(Handle) // 实体是否存在
EntityManager.IsEntityBuilt(Handle) // 是否完全构建
EntityManager.IsEntityActive(Handle) // 是否在模拟中处于活跃状态Direct Fragment/Tag Mutations (Outside Processors)
直接修改片段/标签(处理器外部)
cpp
EntityManager.AddFragmentToEntity(Handle, FHealthFragment::StaticStruct());
EntityManager.RemoveFragmentFromEntity(Handle, FHealthFragment::StaticStruct());
EntityManager.AddTagToEntity(Handle, FDeadTag::StaticStruct());
EntityManager.RemoveTagFromEntity(Handle, FDeadTag::StaticStruct());
EntityManager.SwapTagsForEntity(Handle, FOldTag::StaticStruct(), FNewTag::StaticStruct());cpp
EntityManager.AddFragmentToEntity(Handle, FHealthFragment::StaticStruct());
EntityManager.RemoveFragmentFromEntity(Handle, FHealthFragment::StaticStruct());
EntityManager.AddTagToEntity(Handle, FDeadTag::StaticStruct());
EntityManager.RemoveTagFromEntity(Handle, FDeadTag::StaticStruct());
EntityManager.SwapTagsForEntity(Handle, FOldTag::StaticStruct(), FNewTag::StaticStruct());UMassProcessor
UMassProcessor
Processors iterate over entities matching a query each frame. Subclass (abstract), override and :
UMassProcessorConfigureQueries()Execute()cpp
UCLASS()
class 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;
};处理器每帧遍历匹配查询条件的实体。继承抽象类,并重写和:
UMassProcessorConfigureQueries()Execute()cpp
UCLASS()
class 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;
};Constructor Configuration
构造函数配置
cpp
UMyMovementProcessor::UMyMovementProcessor()
{
ProcessingPhase = EMassProcessingPhase::PrePhysics;
ExecutionFlags = static_cast<int32>(
EProcessorExecutionFlags::Server |
EProcessorExecutionFlags::Standalone);
ExecutionOrder.ExecuteInGroup = UE::Mass::ProcessorGroupNames::Movement;
ExecutionOrder.ExecuteAfter.Add(TEXT("UMassApplyVelocityProcessor"));
bRequiresGameThreadExecution = false; // true if accessing UObjects
}EMassProcessingPhasePrePhysicsStartPhysicsDuringPhysicsEndPhysicsPostPhysicsFrameEndEProcessorExecutionFlagsNoneStandaloneServerClientEditorAllNetModesExecution ordering: , , control processor scheduling relative to named groups and other processors.
ExecutionOrder.ExecuteInGroupExecuteAfterExecuteBeforecpp
UMyMovementProcessor::UMyMovementProcessor()
{
ProcessingPhase = EMassProcessingPhase::PrePhysics;
ExecutionFlags = static_cast<int32>(
EProcessorExecutionFlags::Server |
EProcessorExecutionFlags::Standalone);
ExecutionOrder.ExecuteInGroup = UE::Mass::ProcessorGroupNames::Movement;
ExecutionOrder.ExecuteAfter.Add(TEXT("UMassApplyVelocityProcessor"));
bRequiresGameThreadExecution = false; // 若访问UObject则设为true
}EMassProcessingPhasePrePhysicsStartPhysicsDuringPhysicsEndPhysicsPostPhysicsFrameEndEProcessorExecutionFlagsNoneStandaloneServerClientEditorAllNetModes执行顺序:、、用于控制处理器相对于命名组和其他处理器的调度顺序。
ExecutionOrder.ExecuteInGroupExecuteAfterExecuteBeforeFMassEntityQuery
FMassEntityQuery
Queries define which entities a processor operates on. Configure in , then call :
ConfigureQueries()RegisterQuery()cpp
void UMyMovementProcessor::ConfigureQueries(const TSharedRef<FMassEntityManager>& EntityManager)
{
MovementQuery.AddRequirement<FTransformFragment>(
EMassFragmentAccess::ReadWrite, EMassFragmentPresence::All);
MovementQuery.AddRequirement<FMassVelocityFragment>(
EMassFragmentAccess::ReadOnly, EMassFragmentPresence::All);
MovementQuery.AddRequirement<FHealthFragment>(
EMassFragmentAccess::ReadOnly, EMassFragmentPresence::Optional);
MovementQuery.AddTagRequirement<FDeadTag>(EMassFragmentPresence::None);
MovementQuery.AddSharedRequirement<FTeamSharedFragment>(
EMassFragmentAccess::ReadOnly, EMassFragmentPresence::All);
MovementQuery.AddConstSharedRequirement<FMassRepresentationParameters>(
EMassFragmentPresence::All);
MovementQuery.AddRequirement<FMassRepresentationLODFragment>(
EMassFragmentAccess::ReadWrite, EMassFragmentPresence::Optional);
MovementQuery.AddSubsystemRequirement<UMassRepresentationSubsystem>(
EMassFragmentAccess::ReadWrite);
RegisterQuery(MovementQuery);
} | Usage |
|---|---|
| No access (filter only) |
| |
| |
| Meaning |
|---|---|
| Entity must have this fragment |
| At least one |
| Entity must NOT have this fragment |
| Access if present, skip if absent |
查询定义了处理器操作的实体范围。在中配置查询,然后调用:
ConfigureQueries()RegisterQuery()cpp
void UMyMovementProcessor::ConfigureQueries(const TSharedRef<FMassEntityManager>& EntityManager)
{
MovementQuery.AddRequirement<FTransformFragment>(
EMassFragmentAccess::ReadWrite, EMassFragmentPresence::All);
MovementQuery.AddRequirement<FMassVelocityFragment>(
EMassFragmentAccess::ReadOnly, EMassFragmentPresence::All);
MovementQuery.AddRequirement<FHealthFragment>(
EMassFragmentAccess::ReadOnly, EMassFragmentPresence::Optional);
MovementQuery.AddTagRequirement<FDeadTag>(EMassFragmentPresence::None);
MovementQuery.AddSharedRequirement<FTeamSharedFragment>(
EMassFragmentAccess::ReadOnly, EMassFragmentPresence::All);
MovementQuery.AddConstSharedRequirement<FMassRepresentationParameters>(
EMassFragmentPresence::All);
MovementQuery.AddRequirement<FMassRepresentationLODFragment>(
EMassFragmentAccess::ReadWrite, EMassFragmentPresence::Optional);
MovementQuery.AddSubsystemRequirement<UMassRepresentationSubsystem>(
EMassFragmentAccess::ReadWrite);
RegisterQuery(MovementQuery);
} | 用途 |
|---|---|
| 无访问权限(仅用于过滤) |
| |
| |
| 含义 |
|---|---|
| 实体必须包含该片段 |
| 至少包含一个标记为 |
| 实体必须不包含该片段 |
| 若存在则访问,不存在则跳过 |
Fragment-Based Chunk Filtering
基于片段的块过滤
cpp
// FMassRepresentationLODFragment is a per-entity fragment, not a chunk fragment.
// Filter using a regular fragment view within the iteration lambda.
MovementQuery.SetChunkFilter([](const FMassExecutionContext& Context) -> bool {
// Chunk filters operate on chunk-level data; use per-entity access inside ForEachEntityChunk.
return true;
});cpp
// FMassRepresentationLODFragment是每个实体的片段,而非块片段。
// 在迭代lambda内使用常规片段视图进行过滤。
MovementQuery.SetChunkFilter([](const FMassExecutionContext& Context) -> bool {
// 块过滤基于块级数据;在ForEachEntityChunk内使用每个实体的访问方式。
return true;
});FMassExecutionContext and Iteration
FMassExecutionContext 与迭代
Inside , the context provides typed views into chunk data:
ForEachEntityChunkcpp
void UMyMovementProcessor::Execute(FMassEntityManager& EntityManager,
FMassExecutionContext& Context)
{
MovementQuery.ForEachEntityChunk(Context,
[this](FMassExecutionContext& Context)
{
const int32 NumEntities = Context.GetNumEntities();
TArrayView<FTransformFragment> Transforms =
Context.GetMutableFragmentView<FTransformFragment>();
TConstArrayView<FMassVelocityFragment> Velocities =
Context.GetFragmentView<FMassVelocityFragment>();
TConstArrayView<FMassEntityHandle> Entities = Context.GetEntities();
const float DeltaTime = Context.GetDeltaTimeSeconds();
for (int32 i = 0; i < NumEntities; ++i)
{
Transforms[i].GetMutableTransform().AddToTranslation(
Velocities[i].Value * DeltaTime);
}
});
}Parallel execution: for thread-safe processors.
MovementQuery.ParallelForEachEntityChunk(Context, Lambda)Subsystem access: / for subsystems declared via .
Context.GetMutableSubsystem<T>()Context.GetSubsystem<T>()AddSubsystemRequirementShared/chunk access: , , .
Context.GetMutableSharedFragment<T>()Context.GetConstSharedFragment<T>()Context.GetChunkFragment<T>()在内部,上下文提供了块数据的类型化视图:
ForEachEntityChunkcpp
void UMyMovementProcessor::Execute(FMassEntityManager& EntityManager,
FMassExecutionContext& Context)
{
MovementQuery.ForEachEntityChunk(Context,
[this](FMassExecutionContext& Context)
{
const int32 NumEntities = Context.GetNumEntities();
TArrayView<FTransformFragment> Transforms =
Context.GetMutableFragmentView<FTransformFragment>();
TConstArrayView<FMassVelocityFragment> Velocities =
Context.GetFragmentView<FMassVelocityFragment>();
TConstArrayView<FMassEntityHandle> Entities = Context.GetEntities();
const float DeltaTime = Context.GetDeltaTimeSeconds();
for (int32 i = 0; i < NumEntities; ++i)
{
Transforms[i].GetMutableTransform().AddToTranslation(
Velocities[i].Value * DeltaTime);
}
});
}并行执行:对于线程安全的处理器,使用。
MovementQuery.ParallelForEachEntityChunk(Context, Lambda)子系统访问:通过 / 访问通过声明的子系统。
Context.GetMutableSubsystem<T>()Context.GetSubsystem<T>()AddSubsystemRequirement共享/块访问:、、。
Context.GetMutableSharedFragment<T>()Context.GetConstSharedFragment<T>()Context.GetChunkFragment<T>()FMassCommandBuffer (Deferred Mutations)
FMassCommandBuffer(延迟修改)
CRITICAL: Inside , never call entity manager mutations directly. Structural changes during iteration invalidate archetype memory layouts, causing undefined behavior. Use :
ForEachEntityChunkContext.Defer()cpp
MovementQuery.ForEachEntityChunk(Context,
[](FMassExecutionContext& Context)
{
auto Transforms = Context.GetMutableFragmentView<FTransformFragment>();
auto Entities = Context.GetEntities();
for (int32 i = 0; i < Context.GetNumEntities(); ++i)
{
if (Transforms[i].GetTransform().GetLocation().Z < -1000.f)
{
Context.Defer().AddTag<FDeadTag>(Entities[i]);
Context.Defer().RemoveFragment<FHealthFragment>(Entities[i]);
}
}
});Deferred command execution order: Create -> Add -> Remove -> ChangeComposition -> Set -> Destroy. This guarantees fragments exist before being written, and entities exist before being modified.
PushCommand<T>(Command)PushCommandPushUniqueCommand(TUniquePtr<FMassBatchedCommand>&&)FMassBatchedCommandreferences/mass-entity-patterns.md重要提示:在内部,绝不要直接调用实体管理器的修改方法。迭代期间的结构变更会使原型内存布局失效,导致未定义行为。请使用:
ForEachEntityChunkContext.Defer()cpp
MovementQuery.ForEachEntityChunk(Context,
[](FMassExecutionContext& Context)
{
auto Transforms = Context.GetMutableFragmentView<FTransformFragment>();
auto Entities = Context.GetEntities();
for (int32 i = 0; i < Context.GetNumEntities(); ++i)
{
if (Transforms[i].GetTransform().GetLocation().Z < -1000.f)
{
Context.Defer().AddTag<FDeadTag>(Entities[i]);
Context.Defer().RemoveFragment<FHealthFragment>(Entities[i]);
}
}
});延迟命令的执行顺序:创建 -> 添加 -> 移除 -> 修改组合 -> 设置 -> 销毁。这保证了片段在被写入前已存在,实体在被修改前已存在。
PushCommand<T>(Command)PushCommandFMassBatchedCommandPushUniqueCommand(TUniquePtr<FMassBatchedCommand>&&)references/mass-entity-patterns.mdUMassObserverProcessor
UMassObserverProcessor
Observers react to structural changes -- when a fragment or tag is added to or removed from an entity. They fire automatically:
cpp
UCLASS()
class UHealthAddedObserver : public UMassObserverProcessor
{
GENERATED_BODY()
public:
UHealthAddedObserver()
{
ObservedType = FHealthFragment::StaticStruct();
ObservedOperations = EMassObservedOperationFlags::AddElement;
}
protected:
virtual void ConfigureQueries(const TSharedRef<FMassEntityManager>& EntityManager) override;
virtual void Execute(FMassEntityManager& EntityManager,
FMassExecutionContext& Context) override;
};The observer runs only for entities that just had the observed type added/removed. Use observers for initialization, cleanup, and state-change responses instead of per-frame polling.
Execute观察者响应结构变更——当实体添加或移除片段/标签时,会自动触发:
cpp
UCLASS()
class UHealthAddedObserver : public UMassObserverProcessor
{
GENERATED_BODY()
public:
UHealthAddedObserver()
{
ObservedType = FHealthFragment::StaticStruct();
ObservedOperations = EMassObservedOperationFlags::AddElement;
}
protected:
virtual void ConfigureQueries(const TSharedRef<FMassEntityManager>& EntityManager) override;
virtual void Execute(FMassEntityManager& EntityManager,
FMassExecutionContext& Context) override;
};观察者的方法仅针对刚刚添加/移除了被观察类型的实体运行。请使用观察者进行初始化、清理和状态变更响应,而非每轮帧轮询。
ExecuteFMassEntityView
FMassEntityView
For single-entity access outside processor iteration, use . It is transient -- never store across frames because archetype memory can relocate:
FMassEntityViewcpp
if (EntityManager.IsEntityValid(Handle))
{
FMassEntityView View(EntityManager, Handle);
if (View.GetFragmentDataPtr<FHealthFragment>() != nullptr)
{
FHealthFragment& Health = View.GetFragmentData<FHealthFragment>();
Health.Current -= Damage;
}
bool bDead = View.HasTag<FDeadTag>();
}在处理器迭代外部访问单个实体时,请使用。它是临时对象——绝不要跨帧存储,因为原型内存可能会重新分配:
FMassEntityViewcpp
if (EntityManager.IsEntityValid(Handle))
{
FMassEntityView View(EntityManager, Handle);
if (View.GetFragmentDataPtr<FHealthFragment>() != nullptr)
{
FHealthFragment& Health = View.GetFragmentData<FHealthFragment>();
Health.Current -= Damage;
}
bool bDead = View.HasTag<FDeadTag>();
}Mass Spawner and Config Assets
Mass生成器与配置资源
UMassEntityConfigAssetUMassAssortedFragmentsTraitUMassVisualizationTraitUMassReplicationTraitCustom traits subclass and override to add fragments and configure archetypes. provides editor-time validation.
UMassEntityTraitBaseBuildTemplate(FMassEntityTemplateBuildContext&, const UWorld&)ValidateTemplate()AMassSpawnerreferences/mass-entity-patterns.mdUMassEntityConfigAssetUMassAssortedFragmentsTraitUMassVisualizationTraitUMassReplicationTrait自定义特性需继承,并重写来添加片段并配置原型。提供编辑器级别的验证。
UMassEntityTraitBaseBuildTemplate(FMassEntityTemplateBuildContext&, const UWorld&)ValidateTemplate()AMassSpawnerreferences/mass-entity-patterns.mdCommon Fragments
常用片段
| Fragment | Type | Purpose |
|---|---|---|
| Fragment | Entity world transform |
| Fragment | Linear velocity |
| Fragment | Applied force |
| Fragment | Agent collision radius |
| Fragment | Navigation move target |
| Fragment | Current visual representation state |
| Fragment | Per-entity LOD level and visibility state |
| Const Shared | Representation type per LOD, update rate config |
| Const Shared | Max speed, acceleration |
See for complete field details and trait types.
references/mass-fragment-reference.md| 片段 | 类型 | 用途 |
|---|---|---|
| Fragment | 实体世界变换 |
| Fragment | 线速度 |
| Fragment | 施加的力 |
| Fragment | 代理碰撞半径 |
| Fragment | 导航移动目标 |
| Fragment | 当前视觉表现状态 |
| Fragment | 每个实体的LOD级别和可见性状态 |
| Const Shared | 每个LOD的表现类型、更新速率配置 |
| Const Shared | 最大速度、加速度 |
完整的字段详情和特性类型请查看。
references/mass-fragment-reference.mdRepresentation (ISM Visualization)
视觉表现(ISM可视化)
Mass Entity uses Instanced Static Meshes for rendering thousands of entities without per-entity actors:
| Usage |
|---|---|
| ISM for mid/far entities |
| Full actor for close-up (high LOD) |
| Reduced actor for medium LOD |
| No visual representation |
| Detail Level |
|---|---|
| Full detail, actor-based |
| Reduced detail |
| Minimal (ISM only) |
| Not rendered |
UMassRepresentationSubsystemUMassVisualizationTraitTMassSharedFragmentTraits<T>::GameThreadOnly = trueMass Entity使用实例化静态网格体(ISM)渲染数千个实体,无需为每个实体创建Actor:
| 用途 |
|---|---|
| 中/远距离实体的ISM |
| 近距离(高LOD)的完整Actor |
| 中等LOD的简化Actor |
| 无视觉表现 |
| 细节级别 |
|---|---|
| 全细节,基于Actor |
| 简化细节 |
| 极简(仅ISM) |
| 不渲染 |
UMassRepresentationSubsystemUMassVisualizationTraitTMassSharedFragmentTraits<T>::GameThreadOnly = trueMassCrowd
MassCrowd
UMassCrowdSubsystemEngine/Plugins/AI/MassCrowd/Key features: lane state management, waiting slot allocation, density tracking, and avoidance. Thread-safe for parallel processors: .
TMassExternalSubsystemTraits<UMassCrowdSubsystem>::GameThreadOnly = falseEntities use for lane-following targets. ZoneGraph defines navigation lanes as connected graphs with automatic density management.
FMassMoveTargetFragmentUMassCrowdSubsystemEngine/Plugins/AI/MassCrowd/核心功能:路径状态管理、等待槽分配、密度跟踪和避让。并行处理器可安全使用:。
TMassExternalSubsystemTraits<UMassCrowdSubsystem>::GameThreadOnly = false实体使用存储路径跟随目标。ZoneGraph将导航路径定义为带自动密度管理的连通图。
FMassMoveTargetFragmentStateTree Integration
StateTree集成
Mass Entity processors can trigger State Tree evaluations for entity AI. State Trees provide hierarchical decision-making for Mass entities as an alternative to per-entity behavior trees (prohibitively expensive at scale). For State Tree architecture and task patterns, see .
ue-state-treesMass Entity处理器可为实体AI触发状态树(State Tree)评估。状态树为Mass实体提供分层决策能力,可替代每个实体的行为树(在大规模场景中代价过高)。状态树架构和任务模式请查看。
ue-state-treesCommon Mistakes
常见错误
Direct mutations inside ForEachEntityChunk:
cpp
// WRONG: direct mutation during iteration — undefined behavior
Query.ForEachEntityChunk(Context,
[&EntityManager](FMassExecutionContext& Context) {
EntityManager.AddFragmentToEntity(Context.GetEntities()[0],
FHealthFragment::StaticStruct()); // CRASH
});
// RIGHT: use deferred commands
Query.ForEachEntityChunk(Context,
[](FMassExecutionContext& Context) {
Context.Defer().AddFragment<FHealthFragment>(Context.GetEntities()[0]);
});Storing FMassEntityView across frames: Entity views are transient. Archetype memory may relocate between frames, invalidating stored views. Create a fresh each time.
FMassEntityViewUsing IsSet/IsValid for existence: only checks non-zero fields. A destroyed entity's handle still returns true. Use .
Handle.IsSet()EntityManager.IsEntityValid(Handle)Missing RegisterQuery: Forgetting in silently skips the query. Every query used in must be registered.
RegisterQuery(MyQuery)ConfigureQueries()ExecuteUObject access without game-thread flag: Accessing properties from a parallel processor causes races. Set or declare dependencies via .
UObjectbRequiresGameThreadExecution = trueAddSubsystemRequirementFragment access mismatch: access + triggers an assertion. Match access mode to view type.
ReadOnlyGetMutableFragmentView<T>()在ForEachEntityChunk内直接修改:
cpp
// 错误:迭代期间直接修改——未定义行为
Query.ForEachEntityChunk(Context,
[&EntityManager](FMassExecutionContext& Context) {
EntityManager.AddFragmentToEntity(Context.GetEntities()[0],
FHealthFragment::StaticStruct()); // 崩溃
});
// 正确:使用延迟命令
Query.ForEachEntityChunk(Context,
[](FMassExecutionContext& Context) {
Context.Defer().AddFragment<FHealthFragment>(Context.GetEntities()[0]);
});跨帧存储FMassEntityView:实体视图是临时对象。原型内存可能在帧间重新分配,导致存储的视图失效。每次使用时都要创建新的。
FMassEntityView使用IsSet/IsValid判断实体是否存在:仅检查字段是否非零。已销毁实体的句柄仍会返回true。请使用。
Handle.IsSet()EntityManager.IsEntityValid(Handle)遗漏RegisterQuery:在中忘记调用会导致查询被静默跳过。中使用的每个查询都必须注册。
ConfigureQueries()RegisterQuery(MyQuery)Execute未设置游戏线程标志就访问UObject:在并行处理器中访问属性会导致竞争条件。请设置或通过声明依赖。
UObjectbRequiresGameThreadExecution = trueAddSubsystemRequirement片段访问模式不匹配:访问模式搭配会触发断言。请确保访问模式与视图类型匹配。
ReadOnlyGetMutableFragmentView<T>()Reference Files
参考文件
- -- Processor, observer, trait, and deferred command code templates
references/mass-entity-patterns.md - -- Built-in fragment types, shared fragments, and trait classes
references/mass-fragment-reference.md
- —— 处理器、观察者、特性和延迟命令的代码模板
references/mass-entity-patterns.md - —— 内置片段类型、共享片段和特性类
references/mass-fragment-reference.md
Related Skills
相关技能
- -- NavMesh pathfinding and AI perception for Mass agents
ue-ai-navigation - -- PCG and ISM patterns relevant to Mass representation
ue-procedural-generation - -- GameMode/GameState interaction with Mass simulation
ue-gameplay-framework - -- Actor-entity bridging via MassAgentComponent
ue-actor-component-architecture - -- Parallel execution patterns and thread safety
ue-async-threading - -- USTRUCT, UCLASS, subsystem patterns
ue-cpp-foundations
- —— Mass代理的NavMesh寻路和AI感知
ue-ai-navigation - —— 与Mass表现相关的PCG和ISM模式
ue-procedural-generation - —— GameMode/GameState与Mass模拟的交互
ue-gameplay-framework - —— 通过MassAgentComponent实现Actor与实体的桥接
ue-actor-component-architecture - —— 并行执行模式和线程安全
ue-async-threading - —— USTRUCT、UCLASS、子系统模式
ue-cpp-foundations