On this page
  1. Tag components
  2. Query membership
  3. Structural cost

Tag components

A tag has no per-entity value. Presence supplies the entire signal:

local Selected <const> = tecs.ecs.newTagComponent({name = "Selected"})
local Stunned <const> = tecs.ecs.newTagComponent({name = "Stunned"})

world:set(entity, Selected)
assert(world:has(entity, Selected))
world:remove(entity, Selected)

Use tags for flags, markers, and classifications. Use a scalar or structured component when each entity needs a value.

world:get on a tag returns the tag container rather than row data. Prefer world:has for a presence check.

Query membership

Tags belong in query filters, not column loops:

local activeEnemies <const> = world:newQuery({
    include = {Enemy, Selected},
    exclude = {Stunned},
    type = "logic",
})

for archetype, length, entities in activeEnemies:iter() do
    local positions <const> = archetype:get(Position)
    for row = 1, length do
        updateSelection(entities[row], positions[row])
    end
end

The matching archetype signature already guarantees the tag's presence.

Structural cost

Adding or removing a tag moves the entity between archetypes, just like any component membership change. Use batch operations for large groups:

local targets <const> = world:newQuery({
    include = {Enemy, InBlastRadius},
    temp = true,
})

world:batchSet(targets, Stunned)

local stunnedEnemies <const> = world:newQuery({
    include = {Enemy, Stunned},
    temp = true,
})

world:batchRemove(stunnedEnemies, Stunned)

Disabled causes every ordinary query to exclude the entity unless the query explicitly includes that tag. Paused remains visible to render work; type = "logic" excludes it.

The state stack creates a tag for each named state and adds the current top state's tag to new entities.