ecspresso
    Preparing search index...

    Queries

    Extract entity types from queries to create reusable helper functions:

    import { createQueryDefinition, QueryResultEntity } from 'ecspresso';

    // Create reusable query definitions
    const movingQuery = createQueryDefinition<Components>({
    with: ['position', 'velocity'],
    without: ['frozen']
    });

    // Extract entity type for helper functions
    type MovingEntity = QueryResultEntity<Components, typeof movingQuery>;

    function updatePosition(entity: MovingEntity, deltaTime: number) {
    entity.components.position.x += entity.components.velocity.x * deltaTime;
    entity.components.position.y += entity.components.velocity.y * deltaTime;
    }

    // Use in systems
    world.addSystem('movement')
    .addQuery('entities', movingQuery)
    .setProcess(({ queries, dt }) => {
    for (const entity of queries.entities) {
    updatePosition(entity, dt);
    }
    });

    Get callbacks when entities enter or exit a query match. Unlike regular queries that you poll during update(), reactive queries push notifications when the entity's components change:

    world.addReactiveQuery('enemies', {
    with: ['position', 'enemy'],
    without: ['dead'],
    onEnter: (entity) => {
    console.log(`Enemy ${entity.id} appeared at`, entity.components.position);
    spawnHealthBar(entity.id);
    },
    onExit: (entityId) => {
    // Receives ID since entity may already be removed
    console.log(`Enemy ${entityId} gone`);
    removeHealthBar(entityId);
    },
    });

    // Triggers onEnter: spawning matching entity, adding required component, removing excluded component
    const enemy = world.spawn({ position: { x: 0, y: 0 }, enemy: true }); // onEnter fires

    // Triggers onExit: removing required component, adding excluded component, removing entity
    world.entityManager.addComponent(enemy.id, 'dead', true); // onExit fires

    // Existing matching entities trigger onEnter when query is added
    // Component replacement does NOT trigger enter/exit (match status unchanged)

    // Remove reactive query when no longer needed
    world.removeReactiveQuery('enemies'); // returns true if existed