Queries
Queries let you iterate over entities that have specific components. They are the primary way systems access entity data from the scene.
Basic Query
A query matches all entities in the scene that have all the listed components:
import { defineSystem, addSystem, Query, Mut, LocalTransform, Sprite } from 'esengine';
addSystem(defineSystem([Query(Mut(LocalTransform), Sprite)], (query) => { for (const [entity, transform, sprite] of query) { transform.position.x += 1; }}));The for...of loop yields tuples of [entity, ...components] in the same order as the query parameters.
Mutable Queries
By default, query results are read-only. Use Mut() to get mutable access to specific components:
import { Query, Mut, LocalTransform, Sprite } from 'esengine';
defineSystem( [Query(Mut(LocalTransform), Sprite)], (query) => { for (const [entity, transform, sprite] of query) { transform.position.x += 1; // writable // sprite is read-only } });Iteration Methods
for…of
for (const [entity, transform, sprite] of query) { transform.position.x += 1;}forEach
query.forEach((entity, transform, sprite) => { transform.position.x += 1;});single
Get the one and only matching entity. Returns null if there are zero or more than one matches.
const result = query.single();if (result) { const [entity, transform, sprite] = result; // ...}Utility Methods
query.isEmpty(); // true if no entities matchquery.count(); // number of matching entitiesquery.toArray(); // collect all results into an arrayMultiple Queries
A system can have multiple queries to cross-reference different entity types:
import { defineTag } from 'esengine';
const Player = defineTag('Player');const Enemy = defineTag('Enemy');
defineSystem( [Query(LocalTransform, Player), Query(LocalTransform, Enemy)], (players, enemies) => { for (const [_, playerTransform] of players) { for (const [_, enemyTransform] of enemies) { // Check distance between player and each enemy } } });Combining with Resources
Queries are often used together with resources:
import { Res, Time, Input, Query, Mut, LocalTransform } from 'esengine';
defineSystem( [Res(Time), Res(Input), Query(Mut(LocalTransform), Player)], (time, input, query) => { for (const [entity, transform] of query) { if (input.isKeyDown('KeyD')) { transform.position.x += 200 * time.delta; } } });Example: Collision Detection
Define Hitbox, Player, and Enemy components, attach them to entities in the scene editor, then query:
import { defineSystem, defineComponent, defineTag, addSystem, Query, LocalTransform } from 'esengine';
const Hitbox = defineComponent('Hitbox', { width: 50, height: 50 });const Player = defineTag('Player');const Enemy = defineTag('Enemy');
addSystem(defineSystem( [Query(LocalTransform, Hitbox, Player), Query(LocalTransform, Hitbox, Enemy)], (players, enemies) => { for (const [_, pPos, pBox] of players) { for (const [_, ePos, eBox] of enemies) { const dx = Math.abs(pPos.position.x - ePos.position.x); const dy = Math.abs(pPos.position.y - ePos.position.y);
if (dx < (pBox.width + eBox.width) / 2 && dy < (pBox.height + eBox.height) / 2) { // Collision! } } } }));Next Steps
- Resources — global singleton data like Time and Input
- Components — all builtin and custom component types