Skip to content

Tags

Tags are zero-cost markers that classify entities. They carry no data — they exist purely to categorize entities for use in templates, systems, and queries.

Defining Tags

Tags are empty structs implementing ITag:

public static class GameTags
{
    public struct Player : ITag { }
    public struct Enemy : ITag { }
    public struct Bullet : ITag { }
}

Tags in Templates

Tags are declared on templates via IHasTags:

public partial class SpinnerEntity : ITemplate, IHasTags<SampleTags.Spinner>
{
    public Rotation Rotation;
}

See Templates for details on partitions (IHasPartition) and template inheritance.

Tags in Systems

Systems target specific tag combinations to iterate only matching entities:

// Iterate only entities with the Spinner tag
[ForEachEntity(Tag = typeof(SampleTags.Spinner))]
void Execute(ref Rotation rotation) { ... }

// Iterate entities with both Ball and Active tags
[ForEachEntity(Tags = new[] { typeof(BallTags.Ball), typeof(BallTags.Active) })]
void Execute(in ActiveBall ball) { ... }

Tags can also be used with World.Query() for manual iteration:

// Iterate with an aspect
foreach (var player in PlayerView.Query(World).WithTags<GameTags.Player>())
{
    player.Position += player.Velocity * World.DeltaTime;
}

// Get a single entity
var boss = BossView.Query(World).WithTags<GameTags.Boss>().Single();

See Queries & Iteration for the full query API.

Tags in Queries

int count = world.CountEntitiesWithTags<GameTags.Player>();
world.RemoveEntitiesWithTags<GameTags.Bullet>();

How Tags Affect Storage

Behind the scenes, entities with the same tag combination are stored together in contiguous memory for cache-friendly iteration. This means that iterating all entities with a given tag is fast — they are packed together, and unrelated entities are skipped entirely.

For more on the underlying storage model and low-level APIs like TagSet, Group, and Tag<T>, see Groups & TagSets.