Skip to content

Stream Filters

Sometimes, a dynamic on-the-fly filter is needed to process only a subset of Entities; this enables us to quickly adjust our ECS logic to do different subsets of work without requiring a growing amount of queries to be defined and tracked.

Calling Has, Not, or Where on a Stream returns a FilteredStream<> – a lightweight view that carries all the filter state. Filtering never mutates the original Stream nor the underlying Query – you simply use a new, narrower view to run.

Two Flavors of Filtering

FlavorGranularityHow it narrowsCost
Has & Notwhole Archetypesadditional Has/Not clauses that prune Archetypes before iterationone signature check per Archetype
Whereindividual EntitiesLINQ-style lambda predicates on component values, evaluated live during the runone predicate call per Entity

Both compose freely: the Archetype filters prune entire tables up front, and the Where predicates skim the Entities that remain. All of a FilteredStream's operations honor all of its filters: For, Job, Count, enumeration, and Despawn.

csharp
var stream = world.Stream<Position, Health>();

var wounded = stream
    .Not(Comp<Dead>.Plain)                      // Has & Not (Archetypes)
    .Where((in Health h) => h.Current < h.Max); // Where (lambda)

Both Sides of the Cut

FilteredStream runners can visit the complement, too. Each For and Job variant has an overload taking two delegates: included runs on every Entity passing all filters, and excluded runs on every other Entity the underlying Query matches – whether its whole Archetype was pruned or it just failed a Where predicate. Together, the two delegates visit each Entity exactly once.

csharp
wounded.For(
    included: (ref Position p, ref Health h) => LimpTowardsHealer(ref p),
    excluded: (ref Position p, ref Health h) => h.Regenerate());

Neofox: science One Pass, No Wildcards

Two-delegate runners partition the Query in a single pass – no second iteration, no allocation. Since the partition must visit each Entity exactly once, they are not available on Streams with Wildcard Match Expressions.

Neofox: floof_mug Can I interest you in a Grape-Colored Example

There's an appetizer on this topic! Check out Thanos for an, ahem, "practical" example of using filters.