ue-mass-entity

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

UE 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
.agents/ue-project-context.md
to determine:
  • 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:
  1. What kind of entities are being simulated? (crowds, projectiles, traffic, wildlife, custom)
  2. What data does each entity carry? (position, velocity, health, custom state)
  3. Are entities visualized? If so, what LOD strategy? (ISM, skeletal, actor promotion)
  4. Is this multiplayer? If so, which entities replicate?
  5. How many entities at peak? (hundreds vs. tens of thousands)

向开发者询问:
  1. 正在模拟哪种类型的实体?(人群、投射物、交通、野生动物、自定义)
  2. 每个实体携带哪些数据?(位置、速度、生命值、自定义状态)
  3. 实体是否需要可视化?如果是,采用何种LOD策略?(ISM、骨骼网格体、Actor升级)
  4. 是否为多人游戏?如果是,哪些实体需要同步?
  5. 峰值时的实体数量是多少?(数百个 vs 数万个)

ECS Concepts

ECS 核心概念

Mass Entity uses an archetype ECS model where entity composition determines memory layout:
ConceptClass BasePurpose
Entity
FMassEntityHandle
8-byte identity handle (Index + SerialNumber)
Fragment
FMassFragment
Per-entity mutable data (position, velocity, health)
Tag
FMassTag
Zero-size boolean marker for filtering
Shared Fragment
FMassSharedFragment
Per-archetype mutable data
Const Shared Fragment
FMassConstSharedFragment
Per-archetype immutable data (mesh params)
Chunk Fragment
FMassChunkFragment
Per-memory-chunk data (custom chunk-level state)
Archetype
FMassArchetypeHandle
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
FMassEntityHandle
8字节的身份句柄(索引+序列号)
Fragment
FMassFragment
每个实体的可变数据(位置、速度、生命值)
Tag
FMassTag
用于过滤的零大小布尔标记
Shared Fragment
FMassSharedFragment
每个原型的可变数据
Const Shared Fragment
FMassConstSharedFragment
每个原型的不可变数据(网格参数)
Chunk Fragment
FMassChunkFragment
每个内存块的数据(自定义块级状态)
Archetype
FMassArchetypeHandle
片段/标签类型的唯一组合
原型的重要性:具有相同片段/标签组合的实体共享同一个原型。同一个块内的所有同类型片段会连续存储,从而实现每帧对数千个实体的缓存友好型迭代。

Fragment and Tag Definitions

片段与标签定义

All types require
USTRUCT()
with
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 (
FMassChunkFragment
) store per-memory-chunk state shared across all entities in a chunk. Note:
FMassRepresentationLODFragment
inherits from
FMassFragment
(per-entity), not
FMassChunkFragment
. Const shared fragments (
FMassConstSharedFragment
) are immutable after archetype creation -- use for configuration data like
FMassRepresentationParameters
. See
references/mass-fragment-reference.md
for built-in types.

所有类型都需要带有
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;
};
块片段
FMassChunkFragment
)存储在一个内存块内所有实体共享的块级状态。注意:
FMassRepresentationLODFragment
继承自
FMassFragment
(每个实体独有),而非
FMassChunkFragment
常量共享片段
FMassConstSharedFragment
)在原型创建后不可变——可用于
FMassRepresentationParameters
这类配置数据。如需内置类型,请查看
references/mass-fragment-reference.md

FMassEntityManager

FMassEntityManager

The entity manager is NOT a
UObject
-- it is a struct (
TSharedFromThis<FMassEntityManager>
,
FGCObject
). Access it through
UMassEntitySubsystem
(a
UWorldSubsystem
):
cpp
UMassEntitySubsystem* MassSubsystem = GetWorld()->GetSubsystem<UMassEntitySubsystem>();
FMassEntityManager& EntityManager = MassSubsystem->GetMutableEntityManager();
// const ref: MassSubsystem->GetEntityManager()
实体管理器不是
UObject
——它是一个结构体(
TSharedFromThis<FMassEntityManager>
FGCObject
)。可通过
UMassEntitySubsystem
(一个
UWorldSubsystem
)访问它:
cpp
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()
(aliased as
IsValid()
) only checks non-zero Index/SerialNumber -- it does NOT verify the entity exists. Always use the entity manager:
cpp
EntityManager.IsEntityValid(Handle)   // entity exists
EntityManager.IsEntityBuilt(Handle)   // fully constructed
EntityManager.IsEntityActive(Handle)  // active in simulation
FMassEntityHandle::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
UMassProcessor
(abstract), override
ConfigureQueries()
and
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;
};
处理器每帧遍历匹配查询条件的实体。继承抽象类
UMassProcessor
,并重写
ConfigureQueries()
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
}
EMassProcessingPhase
:
PrePhysics
,
StartPhysics
,
DuringPhysics
,
EndPhysics
,
PostPhysics
,
FrameEnd
EProcessorExecutionFlags
:
None
(0),
Standalone
(1),
Server
(2),
Client
(4),
Editor
(8),
AllNetModes
(7 = Standalone|Server|Client)
Execution ordering:
ExecutionOrder.ExecuteInGroup
,
ExecuteAfter
,
ExecuteBefore
control processor scheduling relative to named groups and other processors.

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; // 若访问UObject则设为true
}
EMassProcessingPhase
PrePhysics
StartPhysics
DuringPhysics
EndPhysics
PostPhysics
FrameEnd
EProcessorExecutionFlags
None
(0)、
Standalone
(1)、
Server
(2)、
Client
(4)、
Editor
(8)、
AllNetModes
(7 = Standalone|Server|Client)
执行顺序
ExecutionOrder.ExecuteInGroup
ExecuteAfter
ExecuteBefore
用于控制处理器相对于命名组和其他处理器的调度顺序。

FMassEntityQuery

FMassEntityQuery

Queries define which entities a processor operates on. Configure in
ConfigureQueries()
, then call
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);
}
EMassFragmentAccess
Usage
None
No access (filter only)
ReadOnly
GetFragmentView<T>()
--
TConstArrayView
ReadWrite
GetMutableFragmentView<T>()
--
TArrayView
EMassFragmentPresence
Meaning
All
Entity must have this fragment
Any
At least one
Any
-marked fragment must exist
None
Entity must NOT have this fragment
Optional
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);
}
EMassFragmentAccess
用途
None
无访问权限(仅用于过滤)
ReadOnly
GetFragmentView<T>()
——
TConstArrayView
ReadWrite
GetMutableFragmentView<T>()
——
TArrayView
EMassFragmentPresence
含义
All
实体必须包含该片段
Any
至少包含一个标记为
Any
的片段
None
实体必须不包含该片段
Optional
若存在则访问,不存在则跳过

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
ForEachEntityChunk
, the context provides typed views into chunk data:
cpp
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:
MovementQuery.ParallelForEachEntityChunk(Context, Lambda)
for thread-safe processors.
Subsystem access:
Context.GetMutableSubsystem<T>()
/
Context.GetSubsystem<T>()
for subsystems declared via
AddSubsystemRequirement
.
Shared/chunk access:
Context.GetMutableSharedFragment<T>()
,
Context.GetConstSharedFragment<T>()
,
Context.GetChunkFragment<T>()
.

ForEachEntityChunk
内部,上下文提供了块数据的类型化视图:
cpp
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
ForEachEntityChunk
, never call entity manager mutations directly. Structural changes during iteration invalidate archetype memory layouts, causing undefined behavior. Use
Context.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)
pushes a typed deferred command. Note:
PushCommand
does NOT accept a lambda. For custom deferred logic, use
PushUniqueCommand(TUniquePtr<FMassBatchedCommand>&&)
with a subclass of
FMassBatchedCommand
. See
references/mass-entity-patterns.md
for patterns.

重要提示:在
ForEachEntityChunk
内部,绝不要直接调用实体管理器的修改方法。迭代期间的结构变更会使原型内存布局失效,导致未定义行为。请使用
Context.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)
用于推送类型化的延迟命令。注意:
PushCommand
不接受lambda表达式。如需自定义延迟逻辑,请结合
FMassBatchedCommand
的子类使用
PushUniqueCommand(TUniquePtr<FMassBatchedCommand>&&)
。相关模式请查看
references/mass-entity-patterns.md

UMassObserverProcessor

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
Execute
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.

观察者响应结构变更——当实体添加或移除片段/标签时,会自动触发:
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;
};
观察者的
Execute
方法仅针对刚刚添加/移除了被观察类型的实体运行。请使用观察者进行初始化、清理和状态变更响应,而非每轮帧轮询。

FMassEntityView

FMassEntityView

For single-entity access outside processor iteration, use
FMassEntityView
. It is transient -- never store across frames because archetype memory can relocate:
cpp
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>();
}

在处理器迭代外部访问单个实体时,请使用
FMassEntityView
。它是临时对象——绝不要跨帧存储,因为原型内存可能会重新分配:
cpp
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生成器与配置资源

UMassEntityConfigAsset
defines entity templates via traits. Add traits like
UMassAssortedFragmentsTrait
(custom fragments),
UMassVisualizationTrait
(ISM visualization), or
UMassReplicationTrait
(networking).
Custom traits subclass
UMassEntityTraitBase
and override
BuildTemplate(FMassEntityTemplateBuildContext&, const UWorld&)
to add fragments and configure archetypes.
ValidateTemplate()
provides editor-time validation.
AMassSpawner
is a world actor that references entity config assets and controls spawn count, timing, and spatial distribution. See
references/mass-entity-patterns.md
for trait implementation templates.

UMassEntityConfigAsset
通过特性定义实体模板。可添加如
UMassAssortedFragmentsTrait
(自定义片段)、
UMassVisualizationTrait
(ISM可视化)或
UMassReplicationTrait
(网络同步)等特性。
自定义特性需继承
UMassEntityTraitBase
,并重写
BuildTemplate(FMassEntityTemplateBuildContext&, const UWorld&)
来添加片段并配置原型。
ValidateTemplate()
提供编辑器级别的验证。
AMassSpawner
是一个世界Actor,它引用实体配置资源并控制生成数量、时机和空间分布。特性实现模板请查看
references/mass-entity-patterns.md

Common Fragments

常用片段

FragmentTypePurpose
FTransformFragment
FragmentEntity world transform
FMassVelocityFragment
FragmentLinear velocity
FMassForceFragment
FragmentApplied force
FAgentRadiusFragment
FragmentAgent collision radius
FMassMoveTargetFragment
FragmentNavigation move target
FMassRepresentationFragment
FragmentCurrent visual representation state
FMassRepresentationLODFragment
FragmentPer-entity LOD level and visibility state
FMassRepresentationParameters
Const SharedRepresentation type per LOD, update rate config
FMassMovementParameters
Const SharedMax speed, acceleration
See
references/mass-fragment-reference.md
for complete field details and trait types.

片段类型用途
FTransformFragment
Fragment实体世界变换
FMassVelocityFragment
Fragment线速度
FMassForceFragment
Fragment施加的力
FAgentRadiusFragment
Fragment代理碰撞半径
FMassMoveTargetFragment
Fragment导航移动目标
FMassRepresentationFragment
Fragment当前视觉表现状态
FMassRepresentationLODFragment
Fragment每个实体的LOD级别和可见性状态
FMassRepresentationParameters
Const Shared每个LOD的表现类型、更新速率配置
FMassMovementParameters
Const Shared最大速度、加速度
完整的字段详情和特性类型请查看
references/mass-fragment-reference.md

Representation (ISM Visualization)

视觉表现(ISM可视化)

Mass Entity uses Instanced Static Meshes for rendering thousands of entities without per-entity actors:
EMassRepresentationType
Usage
StaticMeshInstance
ISM for mid/far entities
HighResSpawnedActor
Full actor for close-up (high LOD)
LowResSpawnedActor
Reduced actor for medium LOD
None
No visual representation
EMassLOD
Detail Level
High
Full detail, actor-based
Medium
Reduced detail
Low
Minimal (ISM only)
Off
Not rendered
UMassRepresentationSubsystem
manages ISM instances. Use
UMassVisualizationTrait
on entity configs to set meshes and LOD distances. Force game-thread for representation processors:
TMassSharedFragmentTraits<T>::GameThreadOnly = true
.

Mass Entity使用实例化静态网格体(ISM)渲染数千个实体,无需为每个实体创建Actor:
EMassRepresentationType
用途
StaticMeshInstance
中/远距离实体的ISM
HighResSpawnedActor
近距离(高LOD)的完整Actor
LowResSpawnedActor
中等LOD的简化Actor
None
无视觉表现
EMassLOD
细节级别
High
全细节,基于Actor
Medium
简化细节
Low
极简(仅ISM)
Off
不渲染
UMassRepresentationSubsystem
管理ISM实例。在实体配置上使用
UMassVisualizationTrait
来设置网格体和LOD距离。如需强制表现处理器在游戏线程运行:
TMassSharedFragmentTraits<T>::GameThreadOnly = true

MassCrowd

MassCrowd

UMassCrowdSubsystem
provides lane-based navigation using ZoneGraph for pedestrian crowd simulation. Located in
Engine/Plugins/AI/MassCrowd/
(not Runtime).
Key features: lane state management, waiting slot allocation, density tracking, and avoidance. Thread-safe for parallel processors:
TMassExternalSubsystemTraits<UMassCrowdSubsystem>::GameThreadOnly = false
.
Entities use
FMassMoveTargetFragment
for lane-following targets. ZoneGraph defines navigation lanes as connected graphs with automatic density management.

UMassCrowdSubsystem
使用ZoneGraph提供基于路径的导航,用于行人人群模拟。位于
Engine/Plugins/AI/MassCrowd/
(非Runtime目录)。
核心功能:路径状态管理、等待槽分配、密度跟踪和避让。并行处理器可安全使用:
TMassExternalSubsystemTraits<UMassCrowdSubsystem>::GameThreadOnly = false
实体使用
FMassMoveTargetFragment
存储路径跟随目标。ZoneGraph将导航路径定义为带自动密度管理的连通图。

StateTree 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-trees
.

Mass Entity处理器可为实体AI触发状态树(State Tree)评估。状态树为Mass实体提供分层决策能力,可替代每个实体的行为树(在大规模场景中代价过高)。状态树架构和任务模式请查看
ue-state-trees

Common 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
FMassEntityView
each time.
Using IsSet/IsValid for existence:
Handle.IsSet()
only checks non-zero fields. A destroyed entity's handle still returns true. Use
EntityManager.IsEntityValid(Handle)
.
Missing RegisterQuery: Forgetting
RegisterQuery(MyQuery)
in
ConfigureQueries()
silently skips the query. Every query used in
Execute
must be registered.
UObject access without game-thread flag: Accessing
UObject
properties from a parallel processor causes races. Set
bRequiresGameThreadExecution = true
or declare dependencies via
AddSubsystemRequirement
.
Fragment access mismatch:
ReadOnly
access +
GetMutableFragmentView<T>()
triggers an assertion. Match access mode to view type.

在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判断实体是否存在
Handle.IsSet()
仅检查字段是否非零。已销毁实体的句柄仍会返回true。请使用
EntityManager.IsEntityValid(Handle)
遗漏RegisterQuery:在
ConfigureQueries()
中忘记调用
RegisterQuery(MyQuery)
会导致查询被静默跳过。
Execute
中使用的每个查询都必须注册。
未设置游戏线程标志就访问UObject:在并行处理器中访问
UObject
属性会导致竞争条件。请设置
bRequiresGameThreadExecution = true
或通过
AddSubsystemRequirement
声明依赖。
片段访问模式不匹配
ReadOnly
访问模式搭配
GetMutableFragmentView<T>()
会触发断言。请确保访问模式与视图类型匹配。

Reference Files

参考文件

  • references/mass-entity-patterns.md
    -- Processor, observer, trait, and deferred command code templates
  • references/mass-fragment-reference.md
    -- Built-in fragment types, shared fragments, and trait classes
  • references/mass-entity-patterns.md
    —— 处理器、观察者、特性和延迟命令的代码模板
  • references/mass-fragment-reference.md
    —— 内置片段类型、共享片段和特性类

Related Skills

相关技能

  • ue-ai-navigation
    -- NavMesh pathfinding and AI perception for Mass agents
  • ue-procedural-generation
    -- PCG and ISM patterns relevant to Mass representation
  • ue-gameplay-framework
    -- GameMode/GameState interaction with Mass simulation
  • ue-actor-component-architecture
    -- Actor-entity bridging via MassAgentComponent
  • ue-async-threading
    -- Parallel execution patterns and thread safety
  • ue-cpp-foundations
    -- USTRUCT, UCLASS, subsystem patterns
  • ue-ai-navigation
    —— Mass代理的NavMesh寻路和AI感知
  • ue-procedural-generation
    —— 与Mass表现相关的PCG和ISM模式
  • ue-gameplay-framework
    —— GameMode/GameState与Mass模拟的交互
  • ue-actor-component-architecture
    —— 通过MassAgentComponent实现Actor与实体的桥接
  • ue-async-threading
    —— 并行执行模式和线程安全
  • ue-cpp-foundations
    —— USTRUCT、UCLASS、子系统模式