-
Notifications
You must be signed in to change notification settings - Fork 26
Open
Description
Problem
Today, common reads look like this:
const x = world.queryFirst(PositionTrait)?.get(PositionTrait)?.x ?? 0This double optionality (queryFirst may return no entity, and get may return no trait) forces ?.?. throughout code—even where components are required by design. It hurts readability, complicates error handling, and makes bugs easier to hide. The README shows selecting traits for updateEach, but there is no general way to iterate entities + guaranteed component tuples for ad‑hoc logic without per-access entity.get(...). See the README’s tuple-like updateEach example for context.
Reference: Koota README — Query and update data.
Proposal
Introduce tuple queries that only iterate entities which have all requested traits and yield tuples with those traits. Add clear single-result helpers:
first()→ returns the first tuple orundefinedsingle()→ returns the single tuple; throws if 0 or >1trySingle()→ returns{ ok: true, value } | { ok: false, error }
Examples — how it should look
// Iterate only entities that have both Position and Velocity.
// Direct, mutation-ready access without ?.?.?.
for (const [entity, position, velocity] of world.queryTuple(Position, Velocity)) {
position.x += velocity.dx * dt
position.y += velocity.dy * dt
}
// Expect exactly one Player with Position:
const [playerEntity, playerPosition] = world.queryTuple(Position, Player).single()
// Optional single result without exceptions:
const maybe = world.queryTuple(Position, Player).trySingle()
if (maybe.ok) {
const [entity, pos] = maybe.value
// ...
}
// Get the first rocket if any:
const firstRocket = world.queryTuple(RocketTag, Position).first()
if (firstRocket) {
const [rocket, position] = firstRocket
// ...
}Prior art (authoritative references)
- Bevy (Rust): Queries yield tuples of components and offer single-entity accessors.
Querydocs: https://docs.rs/bevy/latest/bevy/prelude/struct.Query.htmlSingle/Query::singlepattern: https://docs.rs/bevy/latest/bevy/prelude/struct.Single.html
- EnTT (C++):
view/groupiterate only matching entities;get<T...>assumes presence whiletry_getis the optional variant. - Miniplex (TS): Iterate entities “with” a given set of components; direct access, no null-chaining.
Performance considerations
- Zero extra lookups: selecting traits should be compiled into the query so we do not call
entity.getrepeatedly for each trait per entity. This mirrors the README’supdateEach(([a, b]) => { ... })path. - No per-entity allocations: yielding tuples should avoid allocating new wrapper objects per iteration. Views/references or reusing ephemeral tuple containers are preferable.
- Iteration stability: document the iteration order and behavior when structural changes (add/remove traits) occur during iteration.
single()complexity:single()must detect “more than one” efficiently. Scanning until a second match is acceptable; document this.
Edge cases to define
- Structural changes during iteration: fail-fast vs snapshot semantics.
- Type-level mutability: whether tuple items are read-only vs read-write should align with Koota’s current mutation model.
- Composition with query modifiers: tuple queries should compose with future filters (
With,Without,Changed) if/when a richer DSL lands (see Roadmap).
Related Koota discussion
Disorrder
Metadata
Metadata
Assignees
Labels
No labels