mcpField
B
bevy-ecs-queries

bevy-ecs-queries

github.com/chrisgliddonDevelopment2692 installsUpdated 9w ago
Repository

About bevy-ecs-queries

A skill for working with Bevy 0.19 ECS queries, covering component access in systems, entity filters, and parallel iteration. It focuses on updated query bounds, iteration patterns, and common error handling.

Key features

  1. 01Read and mutate entity components
  2. 02Filter by components and change state
  3. 03Parallelize iteration with par_iter_mut
  4. 04Borrow query subsets with lenses
  5. 05Explain Bevy 0.19 query bounds

Use cases

  1. 01Implement query logic in Bevy systems
  2. 02Filter or process large entity sets in parallel
  3. 03Resolve query compile errors after upgrading

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/chrisgliddon/bevy-skills --skill bevy-ecs-queries

After installing, type/bevy-ecs-queriesin Claude Code to confirm it loaded.

Details

From the project README

Bevy 0.19 — ECS Queries

When to use this skill

  • Reading or writing components from a system.
  • Filtering by presence (With/Without), alternation (Or), or change detection (Changed/Added).
  • Parallelising over a large entity set with par_iter_mut.
  • Borrowing a subset of a query via a lens (transmute_lens).
  • Compiler error mentioning IterQueryData or SingleEntityQueryData after upgrading to 0.19.

Canonical pattern

use bevy::prelude::*;

#[derive(Component, Default)]
struct Health(f32);

#[derive(Component, Default)]
struct Velocity(Vec3);

#[derive(Component)]
struct Player;

#[derive(Component)]
struct Enemy;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Update, (
            move_things,
            on_health_changed,
            damage_visible_enemies,
            integrate_in_parallel,
        ))
        .run();
}

// Sequential iteration. `&` for read, `&mut` for write.
fn move_things(time: Res<Time>, mut q: Query<(&Velocity, &mut Transform)>) {
    let dt = time.delta_secs();
    for (vel, mut tf) in &mut q {
        tf.translation += vel.0 * dt;
    }
}

// Change detection. `Changed<T>` triggers on insert OR mutation.
// `Added<T>` triggers only on insert.
fn on_health_changed(q: Query<(Entity, &Health), Changed<Health>>) {
    for (entity, hp) in &q {
        info!("entity {:?} now has {} hp", entity, hp.0);
    }
}

// Combined filters. `With`/`Without` constrain entities;
// `Or<(...)>` alternates over filters (not components).
fn damage_visible_enemies(
    mut q: Query<&mut Health, (With<Enemy>, Without<Player>, Or<(Added<Enemy>, Changed<Transform>)>)>,
) {
    for mut hp in &mut q {
        hp.0 -= 1.0;
    }
}

// Parallel iteration. Use when N is large (>10k) and per-entity work is non-trivial.
// Cannot use `Commands` or external mutable state — task pool runs items in parallel.
fn integrate_in_parallel(mut q: Query<(&Velocity, &mut Transform)>) {
    q.par_iter_mut().for_each(|(vel, mut tf)| {
        tf.translation += vel.0 * 0.016;
    });
}

// Query lens: temporarily view a query as a narrower one. Useful for
// passing a stricter query into a helper without re-binding the system's
// SystemParam list.
#[allow(dead_code)]
fn use_lens(mut q: Query<(&mut Transform, &Velocity)>) {
    // Read-only narrowed view of just the Transform column.
    let mut lens = q.transmute_lens::<&Transform>();
    let _read_only: Query<&Transform> = lens.query();
}

Bevy 0.19 gotchas

  • Nested query access added bounds in 0.19. Generic mutable iteration usually needs D: IterQueryData; single-entity access, query transmutation/join, traversal, and sorting may need D: SingleEntityQueryData. Read-only iteration retains the broader bounds.
  • Non-iterable query data uses lending iteration. Keep D: QueryData, call let mut iter = query.iter_mut(), then repeatedly call iter.fetch_next() when a nested query item may access several entities.
  • EntityMut::get_components_mut::<(&mut A, &mut B)>() is the safe way to grab two &muts out of one entity and now carries a SingleEntityQueryData bound. It returns Result; do not reach for unchecked aliasing.
  • Query::get/get_mut returns Result, not Option. The error type carries the entity, so don't swallow it with .ok() if you actually need to know why a lookup missed.
  • Or<(With<A>, With<B>)> — Or alternates over filters, not raw component types. Or<(A, B)> does not compile.
  • Changed/Added are tick-based. They compare against the system's last run, so ordinary run conditions do not lose an intervening change. They report mutation, not semantic inequality; use observers/messages when every occurrence or old/new value matters.
  • Don't pair par_iter_mut with Commands. Spawn/despawn from a sequential system that consumes a Resource queue written by the parallel one.

See also

  • bevy-ecs-components — declaring the components queried here.
  • bevy-ecs-systems — using queries inside SystemParam and run conditions.
  • bevy-migration-0-17-to-0-18 — EntityMut::get_components_mut and tick-type move.
  • bevy-migration-0-18-to-0-19 — nested-query trait bounds and resources in queries.

Comments

Markdown supported