Skip to content

Create, Add, Remove, Destroy

Artyom Romskiy edited this page Oct 9, 2026 · 6 revisions

Create, Add, Remove, Destroy

An arena round changes an entity's component set as the unit is recruited, equipped, knocked out, or removed from play. Choose the operation based on whether you are creating a new entity, changing its composition, or ending its lifetime.

The examples continue with the ID-based world from the previous page. Its registrations include the component IDs used below; complete all registration before creating the arena entities.

Create a recruited unit

Use Create<T...> when the initial component set is known. This version gives the new recruit concrete starting values:

Entity goblin = world.Create<Position, Health>(
    positionId,
    healthId,
    new Position { X = 12f, Y = 5f },
    new Health { Value = 80f });

world.Add<Goblin>(goblin);
world.Add<Recruitable>(goblin);

If default component values are correct, use Create<Position, Health>(). For a batch of recruits, pass an output span only when the handles will be needed later:

Span<Entity> recruits = stackalloc Entity[3];

world.Create<Position, Health>(
    positionId,
    healthId,
    recruits.Length,
    recruits);

Without an output span, world.Create<Position, Health>(count) creates the entities and returns the number created; their handles are not retained. A span or array output is useful when the same specific recruits will be equipped or added to a squad list.

Add or initialize components

When a chest pick adds new equipment, use Add with values. One call can add a set of components and initialize them together:

bool equipped = world.Add(
    goblin,
    new Weapon { Damage = 12 },
    new AttackCooldown { Seconds = 0.8f });

The generated API also accepts an explicit type list and registration IDs. Use this when the CLR type alone does not identify the intended registration:

int initializedResources = world.Add<float, float>(
    recruits,
    healthValueId,
    staminaValueId,
    80f,
    35f);

For multiple entities selected by a query, use the query overload when the new components have default values:

Query recruited = world
    .WhereAll<Recruitable, Position>()
    .WhereNone<KnockedOut>();

int markedForCombat = world.Add<Armed>(in recruited);

Add behaves like TryAdd for each requested registration: existing components are left unchanged, while missing registrations are added. A single-entity call returns true when its composition changes; batch and query calls return the number of entities changed.

Remove a component or tag

Removing Recruitable records that the unit has joined the squad, while leaving the entity itself alive:

bool joinedSquad = world.Remove<Recruitable>(goblin);

You can remove a component from an entity list or query as well:

int unequipped = world.Remove<Weapon>(recruits);

Query exhausted = world.WhereAll(healthValueId, staminaValueId);
int removedStamina = world.Remove<float>(in exhausted, staminaValueId);

world.Add<SpeedBoost>(recruits);
Query expiredBoosts = world
    .WhereAll<Position, SpeedBoost>()
    .WhereNone<KnockedOut>();

int removedBoosts = world.Remove<SpeedBoost>(in expiredBoosts);

The ID overload removes the selected stamina registration while leaving the health registration intact. Remove changes the component set only; the entity stays in the world and can still match other queries.

Use a value predicate when a query filter is not enough

Queries filter on component and tag presence. When the rule depends on a value, use the Where pipeline and choose a structural terminal:

Query unitsWithHealth = world.WhereAll<Position, Health>();

// The combat pass has reduced this recruit's health to zero.
world.GetRef<Health>(goblin, healthId).Value = 0f;

int newlyKnockedOut = world
    .Where(
        in unitsWithHealth,
        static (in Health health) => health.Value <= 0f)
    .Add<KnockedOut>()
    .Invoke();

This marks only units whose health reached zero. Avoid calling Add, Remove, or Destroy directly from an active ForEach callback; the Where terminal performs the selected operation as part of the pipeline.

Destroy an entity

Destroy ends the entity's lifetime. Use it when a defeated unit should disappear from the world, rather than merely lose a tag or component:

bool destroyedOne = world.Destroy(goblin);

Query defeated = world
    .WhereAll<KnockedOut>()
    .WhereNone<Recruitable>();

int destroyedFromQuery = world.Destroy(in defeated);
int destroyedFromList = world.Destroy(recruits);

Single-entity operations return bool. Operations on a span, array, or query return the number of entities changed.

Apply one batch transition for a component set

For a squad batch, prefer one multi-component Add over a sequence of separate batch calls. The generated call applies the requested set together instead of repeating the structural operation for each component:

int upgrades = world.Add<Armor, Weapon>(
    recruits,
    new Armor { Value = 5 },
    new Weapon { Damage = 18 });

The same pattern works with positional ComponentId selectors when several registrations share one CLR type. See Components, Tags and Queries for how to choose between typed and ID-based selection.

Previous: Components, Tags and Queries · Next: Entity Component Iteration