first commit
This commit is contained in:
84
docs/gaming/games/ecs/src/query/query.md
Normal file
84
docs/gaming/games/ecs/src/query/query.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Query
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`Query` compiles a declarative `QueryDescriptor` (`include` / optional `exclude` schemas) into bitset masks and evaluates it against the archetype graph. Because archetypes group entities by their exact component set, a query only needs to test each archetype's mask once — every entity inside a matching archetype is automatically a match. The result is O(archetypes) filtering plus O(entities-in-matching-archetypes) collection.
|
||||
|
||||
`defineQuery` is the canonical factory; raw `Query` instances are exposed for advanced consumers building custom system runners.
|
||||
|
||||
## Features
|
||||
|
||||
- Bitset-only match predicate (`containsAll` + `intersects`).
|
||||
- Cached `matchedArchetypes` list, invalidated by `World` whenever archetypes change.
|
||||
- Handles include-only, include + exclude, and empty-exclude descriptors cleanly.
|
||||
- Result is a flat `ReadonlyArray<Entity>` in archetype-discovery order.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { defineComponent, defineQuery, createWorld } from "@suaveplan/ecs";
|
||||
|
||||
const Position = defineComponent("Position", { x: 0, y: 0 });
|
||||
const world = createWorld();
|
||||
const entity = world.spawn();
|
||||
world.addComponent(entity, Position);
|
||||
|
||||
const result = world.query(defineQuery([Position]));
|
||||
console.log([...result]); // [1]
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
defineComponent,
|
||||
defineQuery,
|
||||
Query,
|
||||
createWorld,
|
||||
} from "@suaveplan/ecs";
|
||||
|
||||
const Position = defineComponent("Position", {});
|
||||
const Disabled = defineComponent("Disabled", {});
|
||||
|
||||
const world = createWorld();
|
||||
world.registerComponent(Position);
|
||||
world.registerComponent(Disabled);
|
||||
|
||||
// Build a long-lived Query and re-evaluate on every tick.
|
||||
const activeMovers = new Query(
|
||||
defineQuery([Position], [Disabled]),
|
||||
world.registry,
|
||||
);
|
||||
|
||||
function tick() {
|
||||
const entities = activeMovers.evaluate(
|
||||
// archetype graph is exposed indirectly via world.query; advanced
|
||||
// consumers run their own archetype scans through reflection.
|
||||
[],
|
||||
);
|
||||
// ... iterate entities ...
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
| Member | Description |
|
||||
|---|---|
|
||||
| `new Query(descriptor, registry)` | Compile masks from a descriptor. Throws if descriptor references unregistered schemas. |
|
||||
| `defineQuery(include, exclude?)` | Build a descriptor with optional `exclude`. |
|
||||
| `matches(archetype)` | Predicate: does this archetype's mask satisfy the query? |
|
||||
| `evaluate(archetypes)` | Collect every entity from every matching archetype. Result is cached until `invalidate()`. |
|
||||
| `invalidate()` | Drop the cached match list; the next `evaluate` rebuilds it. |
|
||||
| `includeMask` / `excludeMask` | Read-only masks for debug / inspection. |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `excludeMask` is `null` (not an empty `BitSet`) when no exclude is declared, so the predicate skips the `intersects` test entirely in the common case.
|
||||
- Empty `exclude: []` is treated as "no exclude" — the descriptor normalises the difference.
|
||||
- The world owns its `Query` objects; one-off `world.query(descriptor)` calls build an ad-hoc `Query` per call and discard it. Long-lived systems should pin a `Query` instance and call `evaluate` repeatedly.
|
||||
- The cached `matchedArchetypes` list is invalidated whenever the archetype graph mutates (component add/remove that creates a new archetype, or `clear`). Mutations within an existing archetype do not invalidate — entity lists are read fresh on every evaluate.
|
||||
- Iteration order is "every entity in archetype 0, then every entity in archetype 1, …". Within an archetype the order matches insertion order (subject to swap-remove side-effects).
|
||||
Reference in New Issue
Block a user