AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Ue Mass Entity

skill-quodsoler-unreal-engine-skills-ue-mass-entity · by quodsoler

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-re…

No reviews yet
0 installs
21 views
0.0% view→install

Install

$ agentstack add skill-quodsoler-unreal-engine-skills-ue-mass-entity

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-quodsoler-unreal-engine-skills-ue-mass-entity)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
6mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Ue Mass Entity? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

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:

| Concept | Class Base | Purpose | |---------|-----------|---------| | 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.


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, FGCObject). Access it through UMassEntitySubsystem (a UWorldSubsystem):

UMassEntitySubsystem* MassSubsystem = GetWorld()->GetSubsystem();
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 — retain it until
// observer processors should fire (dropping it early suppresses observer execution).
TArray Entities;
TSharedRef 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& EntityManager) override;
    virtual void Execute(FMassEntityManager& EntityManager,
                         FMassExecutionContext& Context) override;
private:
    FMassEntityQuery MovementQuery;
};

Constructor Configuration

UMyMovementProcessor::UMyMovementProcessor()
{
    ProcessingPhase = EMassProcessingPhase::PrePhysics;
    ExecutionFlags = static_cast(
        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& EntityManager)
{
    MovementQuery.AddRequirement(
        EMassFragmentAccess::ReadWrite, EMassFragmentPresence::All);
    MovementQuery.AddRequirement(
        EMassFragmentAccess::ReadOnly, EMassFragmentPresence::All);
    MovementQuery.AddRequirement(
        EMassFragmentAccess::ReadOnly, EMassFragmentPresence::Optional);
    MovementQuery.AddTagRequirement(EMassFragmentPresence::None);
    MovementQuery.AddSharedRequirement(
        EMassFragmentAccess::ReadOnly, EMassFragmentPresence::All);
    MovementQuery.AddConstSharedRequirement(
        EMassFragmentPresence::All);
    MovementQuery.AddRequirement(
        EMassFragmentAccess::ReadWrite, EMassFragmentPresence::Optional);
    MovementQuery.AddSubsystemRequirement(
        EMassFragmentAccess::ReadWrite);
    RegisterQuery(MovementQuery);
}

| EMassFragmentAccess | Usage | |----------------------|-------| | None | No access (filter only) | | ReadOnly | GetFragmentView() -- TConstArrayView | | ReadWrite | GetMutableFragmentView() -- 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 |

Fragment-Based Chunk Filtering

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

FMassExecutionContext and Iteration

Inside ForEachEntityChunk, the context provides typed views into chunk data:

void UMyMovementProcessor::Execute(FMassEntityManager& EntityManager,
                                   FMassExecutionContext& Context)
{
    MovementQuery.ForEachEntityChunk(Context,
        [this](FMassExecutionContext& Context)
    {
        const int32 NumEntities = Context.GetNumEntities();
        TArrayView Transforms =
            Context.GetMutableFragmentView();
        TConstArrayView Velocities =
            Context.GetFragmentView();
        TConstArrayView Entities = Context.GetEntities();
        const float DeltaTime = Context.GetDeltaTimeSeconds();

        for (int32 i = 0; i ()` / `Context.GetSubsystem()` for subsystems declared via `AddSubsystemRequirement`.

**Shared/chunk access:** `Context.GetMutableSharedFragment()`, `Context.GetConstSharedFragment()`, `Context.GetChunkFragment()`.

---

## FMassCommandBuffer (Deferred Mutations)

**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();
    auto Entities = Context.GetEntities();
    for (int32 i = 0; i (Entities[i]);
            Context.Defer().RemoveFragment(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(Command) pushes a typed deferred command. Note: PushCommand does NOT accept a lambda. For custom deferred logic, use PushUniqueCommand(TUniquePtr&&) with a subclass of FMassBatchedCommand. See references/mass-entity-patterns.md for patterns.


UMassObserverProcessor

Observers react to structural changes -- when a fragment or tag is added to or removed from an entity. They fire automatically:

UCLASS()
class UHealthAddedObserver : public UMassObserverProcessor
{
    GENERATED_BODY()
public:
    UHealthAddedObserver()
    {
        ObservedType = FHealthFragment::StaticStruct();
        ObservedOperations = EMassObservedOperationFlags::AddElement;
    }
protected:
    virtual void ConfigureQueries(const TSharedRef& 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.


FMassEntityView

For single-entity access outside processor iteration, use FMassEntityView. It is transient -- never store across frames because archetype memory can relocate:

if (EntityManager.IsEntityValid(Handle))
{
    FMassEntityView View(EntityManager, Handle);
    if (View.GetFragmentDataPtr() != nullptr)
    {
        FHealthFragment& Health = View.GetFragmentData();
        Health.Current -= Damage;
    }
    bool bDead = View.HasTag();
}

Mass Spawner and Config Assets

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.


Common Fragments

| Fragment | Type | Purpose | |----------|------|---------| | FTransformFragment | Fragment | Entity world transform | | FMassVelocityFragment | Fragment | Linear velocity | | FMassForceFragment | Fragment | Applied force | | FAgentRadiusFragment | Fragment | Agent collision radius | | FMassMoveTargetFragment | Fragment | Navigation move target | | FMassRepresentationFragment | Fragment | Current visual representation state | | FMassRepresentationLODFragment | Fragment | Per-entity LOD level and visibility state | | FMassRepresentationParameters | Const Shared | Representation type per LOD, update rate config | | FMassMovementParameters | Const Shared | Max speed, acceleration |

See references/mass-fragment-reference.md for complete field details and trait types.


Representation (ISM Visualization)

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::GameThreadOnly = true.


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::GameThreadOnly = false.

Entities use FMassMoveTargetFragment for lane-following targets. ZoneGraph defines navigation lanes as connected graphs with automatic density management.


StateTree Integration

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.


Common Mistakes

Direct mutations inside ForEachEntityChunk:

// 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(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) i

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.