quodsoler/unreal-engine-skills3 files

Ue Mass Entity

Use this skill when working with Mass Entity, MassEntity, Mass AI, MassProcessor, MassFragment, MassTag, MassObserver, MassSpawner, MassCrowd, Mass ECS, entity archetype, ForEachEntityChunk, FMassEntityQuery, FMassEntityManager, ISM crowd, or large-scale entity simulation in Unreal Engine. See references/mass-entity-patterns.md for processor and observer templates. See references/mass-fragment-reference.md for built-in fragment types.

Specification
Skill ID
quodsoler/unreal-engine-skills/ue-mass-entity
Publisher
quodsoler
Repository
unreal-engine-skills
Installs
0
Files
3
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

quodsoler/unreal-engine-skills/ue-mass-entityInstalls these files
  • SKILL.md
  • references/mass-entity-patterns.md
  • references/mass-fragment-reference.md

What this skill tells the agent

UE Mass Entity Framework

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.

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

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)

ECS Concepts

Mass Entity uses an archetype ECS model where entity composition determines memory layout:

ConceptClass BasePurpose
EntityFMassEntityHandle8-byte identity handle (Index + SerialNumber)
FragmentFMassFragmentPer-entity mutable data (position, velocity, health)
TagFMassTagZero-size boolean marker for filtering
Shared FragmentFMassSharedFragmentPer-archetype mutable data
Const Shared FragmentFMassConstSharedFragmentPer-archetype immutable data (mesh params)
Chunk FragmentFMassChunkFragmentPer-memory-chunk data (custom chunk-level state)
ArchetypeFMassArchetypeHandleUnique 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.


Fragment and Tag Definitions

All types require USTRUCT() with 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;
};

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.


FMassEntityManager

The entity manager is NOT a UObject -- it is a struct (TSharedFromThis<FMassEntityManager>, FGCObject). Access it through UMassEntitySubsystem (a UWorldSubsystem):

UMassEntitySubsystem* MassSubsystem = GetWorld()->GetSubsystem<UMassEntitySubsystem>();
FMassEntityManager& EntityManager = MassSubsystem->GetMutableEntityManager();
// const ref: MassSubsystem->GetEntityManager()

Entity Lifecycle

// 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);

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:

EntityManager.IsEntityValid(Handle)   // entity exists
EntityManager.IsEntityBuilt(Handle)   // fully constructed
EntityManager.IsEntityActive(Handle)  // active in simulation

Direct Fragment/Tag Mutations (Outside Processors)

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

Processors iterate over entities matching a query each frame. Subclass UMassProcessor (abstract), override ConfigureQueries() and 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;
};

Constructor Configuration

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.


FMassEntityQuery

Queries define which entities a processor operates on. Configure in ConfigureQueries(), then call 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);
}

| EMassFragmentAccess | Usage |

Use this skill in a project

Open any RiverX project, search the Skills panel for quodsoler/unreal-engine-skills/ue-mass-entity, and install it in one click.

Use this skill