Loading...
Loading...
Compare original and translation side by side
.agents/ue-project-context.md.agents/ue-project-context.md| 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 |
| 概念 | 基类 | 用途 |
|---|---|---|
| Entity | | 8字节的身份句柄(索引+序列号) |
| Fragment | | 每个实体的可变数据(位置、速度、生命值) |
| Tag | | 用于过滤的零大小布尔标记 |
| Shared Fragment | | 每个原型的可变数据 |
| Const Shared Fragment | | 每个原型的不可变数据(网格参数) |
| Chunk Fragment | | 每个内存块的数据(自定义块级状态) |
| Archetype | | 片段/标签类型的唯一组合 |
USTRUCT()GENERATED_BODY()// 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;
};FMassChunkFragmentFMassRepresentationLODFragmentFMassFragmentFMassChunkFragmentFMassConstSharedFragmentFMassRepresentationParametersreferences/mass-fragment-reference.mdGENERATED_BODY()USTRUCT()// 每个实体的可变数据
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.mdUObjectTSharedFromThis<FMassEntityManager>FGCObjectUMassEntitySubsystemUWorldSubsystemUMassEntitySubsystem* MassSubsystem = GetWorld()->GetSubsystem<UMassEntitySubsystem>();
FMassEntityManager& EntityManager = MassSubsystem->GetMutableEntityManager();
// const ref: MassSubsystem->GetEntityManager()UObjectTSharedFromThis<FMassEntityManager>FGCObjectUMassEntitySubsystemUWorldSubsystemUMassEntitySubsystem* MassSubsystem = GetWorld()->GetSubsystem<UMassEntitySubsystem>();
FMassEntityManager& EntityManager = MassSubsystem->GetMutableEntityManager();
// 常量引用: MassSubsystem->GetEntityManager()// 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);// 一次性创建
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);FMassEntityHandle::IsSet()IsValid()EntityManager.IsEntityValid(Handle) // entity exists
EntityManager.IsEntityBuilt(Handle) // fully constructed
EntityManager.IsEntityActive(Handle) // active in simulationFMassEntityHandle::IsSet()IsValid()EntityManager.IsEntityValid(Handle) // 实体是否存在
EntityManager.IsEntityBuilt(Handle) // 是否完全构建
EntityManager.IsEntityActive(Handle) // 是否在模拟中处于活跃状态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());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());UMassProcessorConfigureQueries()Execute()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()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;
};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
}EMassProcessingPhasePrePhysicsStartPhysicsDuringPhysicsEndPhysicsPostPhysicsFrameEndEProcessorExecutionFlagsNoneStandaloneServerClientEditorAllNetModesExecutionOrder.ExecuteInGroupExecuteAfterExecuteBeforeUMyMovementProcessor::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
}EMassProcessingPhasePrePhysicsStartPhysicsDuringPhysicsEndPhysicsPostPhysicsFrameEndEProcessorExecutionFlagsNoneStandaloneServerClientEditorAllNetModesExecutionOrder.ExecuteInGroupExecuteAfterExecuteBeforeConfigureQueries()RegisterQuery()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()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);
} | 用途 |
|---|---|
| 无访问权限(仅用于过滤) |
| |
| |
| 含义 |
|---|---|
| 实体必须包含该片段 |
| 至少包含一个标记为 |
| 实体必须不包含该片段 |
| 若存在则访问,不存在则跳过 |
// 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;
});// FMassRepresentationLODFragment是每个实体的片段,而非块片段。
// 在迭代lambda内使用常规片段视图进行过滤。
MovementQuery.SetChunkFilter([](const FMassExecutionContext& Context) -> bool {
// 块过滤基于块级数据;在ForEachEntityChunk内使用每个实体的访问方式。
return true;
});ForEachEntityChunkvoid 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>()AddSubsystemRequirementContext.GetMutableSharedFragment<T>()Context.GetConstSharedFragment<T>()Context.GetChunkFragment<T>()ForEachEntityChunkvoid 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>()AddSubsystemRequirementContext.GetMutableSharedFragment<T>()Context.GetConstSharedFragment<T>()Context.GetChunkFragment<T>()ForEachEntityChunkContext.Defer()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)PushCommandPushUniqueCommand(TUniquePtr<FMassBatchedCommand>&&)FMassBatchedCommandreferences/mass-entity-patterns.mdForEachEntityChunkContext.Defer()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.mdUCLASS()
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;
};ExecuteUCLASS()
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;
};ExecuteFMassEntityViewif (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>();
}FMassEntityViewif (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>();
}UMassEntityConfigAssetUMassAssortedFragmentsTraitUMassVisualizationTraitUMassReplicationTraitUMassEntityTraitBaseBuildTemplate(FMassEntityTemplateBuildContext&, const UWorld&)ValidateTemplate()AMassSpawnerreferences/mass-entity-patterns.mdUMassEntityConfigAssetUMassAssortedFragmentsTraitUMassVisualizationTraitUMassReplicationTraitUMassEntityTraitBaseBuildTemplate(FMassEntityTemplateBuildContext&, const UWorld&)ValidateTemplate()AMassSpawnerreferences/mass-entity-patterns.md| 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 |
references/mass-fragment-reference.md| 片段 | 类型 | 用途 |
|---|---|---|
| Fragment | 实体世界变换 |
| Fragment | 线速度 |
| Fragment | 施加的力 |
| Fragment | 代理碰撞半径 |
| Fragment | 导航移动目标 |
| Fragment | 当前视觉表现状态 |
| Fragment | 每个实体的LOD级别和可见性状态 |
| Const Shared | 每个LOD的表现类型、更新速率配置 |
| Const Shared | 最大速度、加速度 |
references/mass-fragment-reference.md | 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 = true | 用途 |
|---|---|
| 中/远距离实体的ISM |
| 近距离(高LOD)的完整Actor |
| 中等LOD的简化Actor |
| 无视觉表现 |
| 细节级别 |
|---|---|
| 全细节,基于Actor |
| 简化细节 |
| 极简(仅ISM) |
| 不渲染 |
UMassRepresentationSubsystemUMassVisualizationTraitTMassSharedFragmentTraits<T>::GameThreadOnly = trueUMassCrowdSubsystemEngine/Plugins/AI/MassCrowd/TMassExternalSubsystemTraits<UMassCrowdSubsystem>::GameThreadOnly = falseFMassMoveTargetFragmentUMassCrowdSubsystemEngine/Plugins/AI/MassCrowd/TMassExternalSubsystemTraits<UMassCrowdSubsystem>::GameThreadOnly = falseFMassMoveTargetFragmentue-state-treesue-state-trees// 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]);
});FMassEntityViewHandle.IsSet()EntityManager.IsEntityValid(Handle)RegisterQuery(MyQuery)ConfigureQueries()ExecuteUObjectbRequiresGameThreadExecution = trueAddSubsystemRequirementReadOnlyGetMutableFragmentView<T>()// 错误:迭代期间直接修改——未定义行为
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]);
});FMassEntityViewHandle.IsSet()EntityManager.IsEntityValid(Handle)ConfigureQueries()RegisterQuery(MyQuery)ExecuteUObjectbRequiresGameThreadExecution = trueAddSubsystemRequirementReadOnlyGetMutableFragmentView<T>()references/mass-entity-patterns.mdreferences/mass-fragment-reference.mdreferences/mass-entity-patterns.mdreferences/mass-fragment-reference.mdue-ai-navigationue-procedural-generationue-gameplay-frameworkue-actor-component-architectureue-async-threadingue-cpp-foundationsue-ai-navigationue-procedural-generationue-gameplay-frameworkue-actor-component-architectureue-async-threadingue-cpp-foundations