View
A View provides lightweight, efficient iteration over entities that have specific components. It uses the sparse set architecture to achieve cache-friendly traversal without copying data.
Overview
Views enable component-based queries using a fluent API:
// Iterate all entities with Transform and Velocity
for (auto [entity, transform, velocity] : gameWorld->view<
TransformComponent,
VelocityComponent
>()) {
velocity->position += velocity->direction * deltaTime;
}
// With filters
for (auto [entity, health, active] : gameWorld->view<
HealthComponent,
Active
>().whereEnabled().exclude<Invincible>()) {
// Process only enabled, non-invincible entities
}
Architecture
The View uses the lead set pattern:
- The first component type becomes the "lead" iterator
- For each entity in the lead set, check existence in other sets
- Apply exclusion and filter predicates
- Yield matching entities with their components
Lead Set (Transform): [E0, E2, E5, E7, E9]
↓ ↓ ↓ ↓ ↓
Velocity Set: [E0, E2, __, E7, __] ← Cross-reference
✓ ✓ ✗ ✓ ✗
Result: [E0, E2, E7]
API Reference
Construction
Views are typically obtained from GameWorld:
auto view = gameWorld->view<ComponentA, ComponentB, ComponentC>();
Or directly from EntityManager:
View<ComponentA, ComponentB> view(&entityManager);
Filtering
exclude()
Excludes entities that have a specific component:
// Skip entities with Shield component
for (auto [e, health] : world->view<HealthComponent>()
.exclude<ShieldComponent>()) {
// Only unshielded entities
}
whereEnabled()
Filters to only include entities where all queried components are enabled:
for (auto [e, move, active] : world->view<
Move2DComponent,
Active
>().whereEnabled()) {
// Only if move.isEnabled() && active.isEnabled()
}
Components without an isEnabled() method are assumed enabled.
Iteration
Views support range-based for loops with structured bindings:
for (auto [entity, compA, compB] : view) {
// entity: GameObject
// compA: ComponentA*
// compB: ComponentB*
}
The tuple contains:
GameObject- wrapper for the entityComponent*...- pointers to each queried component
Usage Patterns
Basic Query
for (auto [entity, transform] : world->view<TransformComponent>()) {
transform->setPosition(newPos);
}
Multi-Component Query
for (auto [entity, transform, velocity, gravity] : world->view<
TransformComponent,
VelocityComponent,
GravityComponent
>()) {
velocity->velocity += gravity->force * deltaTime;
transform->position += velocity->velocity * deltaTime;
}
With Active Tag
// Common pattern: include Active tag to skip inactive entities
for (auto [entity, health, damage, active] : world->view<
HealthComponent,
DamageDealerComponent,
Active
>().whereEnabled()) {
// Process only active entities
}
Exclusion
// Find all enemies without AI (for debugging)
for (auto [entity, enemy] : world->view<EnemyComponent>()
.exclude<AIComponent>()) {
LOG_WARN("Enemy {} has no AI!", entity.entityHandle().entityId);
}
Chained Filters
for (auto [e, h, a] : world->view<HealthComponent, Active>()
.whereEnabled()
.exclude<Invincible>()
.exclude<Dead>()) {
// Enabled, not invincible, not dead
}
Performance Considerations
Lead Set Selection
The first component type determines iteration order. Choose the smallest set as the lead for best performance:
// If few entities have RareComponent, put it first
for (auto [e, rare, common] : world->view<
RareComponent, // Lead - smallest set
CommonComponent
>()) { }
Sparse Set Iteration
Views iterate the dense storage of the lead set, which is:
- Cache-friendly - contiguous memory access
- No indirection - direct component access
- O(n) - linear in the number of entities with the lead component
Cross-Reference Cost
Each additional component type adds an O(1) existence check per entity. For views with many component types, this can add up.
Implementation Details
Iterator Structure
struct Iterator {
LeadIterator current_; // Points into lead SparseSet
LeadIterator end_;
const View* view_;
bool isValid() const; // Check all predicates
void advance(); // Skip to next valid entity
};
Validity Checks
For each entity, the iterator checks:
- Entity validity - Handle is still valid in registry
- Include check - Entity has all required components
- Exclude check - Entity has none of the excluded components
- Enabled check - All components pass
isEnabled()(if filtered)
Thread Safety
Views are not thread-safe. The underlying EntityManager and SparseSets must not be modified during iteration.
See Also
- EntityManager - Component storage
- SparseSet - Underlying data structure
- GameObject - Entity wrapper returned by views
- System - Systems that use views for queries
- Traits - isEnabled() detection
- Component Lifecycle - isEnabled() and other hooks