Claude
Skills
Sign in
Back

bevy-ecs-patterns

Included with Lifetime
$97 forever

Advanced Bevy ECS: complex queries, system scheduling, change detection, and performance tuning. Use when optimizing Bevy architecture or implementing complex game systems.

General

What this skill does


# Bevy ECS Patterns

Advanced patterns and techniques for Bevy's Entity Component System, focusing on performance, maintainability, and complex game architectures.

## When to Use This Skill

| Use this skill when... | Use bevy-game-engine instead when... |
|------------------------|--------------------------------------|
| Optimizing ECS query performance | Setting up a new Bevy project from scratch |
| Implementing complex entity relationships or hierarchies | Learning basic ECS concepts (components, systems, resources) |
| Designing system scheduling and ordering | Handling input, assets, or game states |
| Using change detection (`Changed<T>`, `Added<T>`) | Writing basic game logic or app structure |
| Working with `ParamSet`, parallel iteration, or batch operations | Configuring rendering, UI, or audio |
| Debugging archetype fragmentation or storage strategies | Adding plugins or common Bevy dependencies |

## Core Expertise

**Advanced Queries**
- Complex query filters and combinations
- Query state and caching
- Entity relationships and hierarchies
- Parallel iteration strategies

**System Scheduling**
- System ordering and dependencies
- Run conditions and gating
- System sets and scheduling
- Fixed timestep systems

**Change Detection**
- `Changed<T>` and `Added<T>` filters
- `Ref<T>` for change tracking
- Efficient reactive systems

## Advanced Query Patterns

**Query Filters**
```rust
use bevy::prelude::*;

// Multiple filters
fn targeting_system(
    query: Query<
        (&Transform, &mut Target),
        (With<Enemy>, Without<Dead>, Changed<Health>)
    >,
) {
    for (transform, mut target) in &query {
        // Only enemies that are alive and recently damaged
    }
}

// Optional components
fn flexible_system(
    query: Query<(&Transform, Option<&Velocity>, Option<&Acceleration>)>,
) {
    for (transform, velocity, acceleration) in &query {
        if let Some(vel) = velocity {
            // Has velocity
        }
    }
}

// Or filters
fn pickup_system(
    query: Query<Entity, Or<(With<HealthPickup>, With<AmmoPickup>)>>,
) {
    for entity in &query {
        // Process any pickup type
    }
}
```

**Multiple Queries with Conflicts**
```rust
// Use ParamSet when queries have conflicting access
fn combat_system(
    mut param_set: ParamSet<(
        Query<&mut Health, With<Player>>,
        Query<&mut Health, With<Enemy>>,
    )>,
) {
    // Access one query at a time
    for mut health in param_set.p0().iter_mut() {
        health.0 += 1.0; // Heal player
    }

    for mut health in param_set.p1().iter_mut() {
        health.0 -= 10.0; // Damage enemies
    }
}

// Query transmutation for flexible access
fn dynamic_query(
    all_entities: Query<(Entity, &Transform)>,
    players: Query<&Player>,
) {
    for (entity, transform) in &all_entities {
        if players.get(entity).is_ok() {
            // This is a player
        }
    }
}
```

**Entity Relationships**
```rust
// Parent-child hierarchies
#[derive(Component)]
struct Inventory;

#[derive(Component)]
struct InventorySlot(usize);

fn spawn_inventory(mut commands: Commands) {
    commands.spawn((Inventory, SpatialBundle::default()))
        .with_children(|parent| {
            for i in 0..10 {
                parent.spawn((InventorySlot(i), SpriteBundle::default()));
            }
        });
}

// Query parent from child
fn child_system(
    children: Query<(&InventorySlot, &Parent)>,
    parents: Query<&Inventory>,
) {
    for (slot, parent) in &children {
        if let Ok(inventory) = parents.get(parent.get()) {
            // Access parent inventory
        }
    }
}

// Query children from parent
fn parent_system(
    parents: Query<&Children, With<Inventory>>,
    slots: Query<&InventorySlot>,
) {
    for children in &parents {
        for &child in children.iter() {
            if let Ok(slot) = slots.get(child) {
                // Access child slot
            }
        }
    }
}
```

## System Scheduling

**System Ordering**
```rust
fn configure_systems(app: &mut App) {
    app.add_systems(Update, (
        // Sequential execution
        read_input,
        apply_movement.after(read_input),
        check_collisions.after(apply_movement),
        update_ui.after(check_collisions),
    ));

    // Alternative: chain for strict ordering
    app.add_systems(Update, (
        read_input,
        apply_movement,
        check_collisions,
        update_ui,
    ).chain());

    // Parallel by default when no dependencies
    app.add_systems(Update, (
        update_animations,  // Runs in parallel
        update_particles,   // with these systems
        update_audio,
    ));
}
```

**System Sets**
```rust
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
enum PhysicsSet {
    Movement,
    CollisionDetection,
    CollisionResolution,
}

fn configure_physics(app: &mut App) {
    app.configure_sets(Update, (
        PhysicsSet::Movement,
        PhysicsSet::CollisionDetection.after(PhysicsSet::Movement),
        PhysicsSet::CollisionResolution.after(PhysicsSet::CollisionDetection),
    ));

    app.add_systems(Update, (
        apply_velocity.in_set(PhysicsSet::Movement),
        apply_gravity.in_set(PhysicsSet::Movement),
        broad_phase.in_set(PhysicsSet::CollisionDetection),
        narrow_phase.in_set(PhysicsSet::CollisionDetection),
        resolve_collisions.in_set(PhysicsSet::CollisionResolution),
    ));
}
```

**Run Conditions**
```rust
fn configure_conditional_systems(app: &mut App) {
    app.add_systems(Update, (
        // State-based conditions
        game_logic.run_if(in_state(GameState::Playing)),

        // Resource-based conditions
        debug_ui.run_if(resource_exists::<DebugMode>),

        // Custom conditions
        spawn_enemies.run_if(should_spawn_enemy),

        // Combined conditions
        player_input.run_if(
            in_state(GameState::Playing)
                .and_then(not(resource_exists::<Paused>))
        ),
    ));
}

fn should_spawn_enemy(
    time: Res<Time>,
    enemy_count: Query<&Enemy>,
) -> bool {
    enemy_count.iter().count() < 10 && time.elapsed_seconds() > 5.0
}
```

**Fixed Timestep**
```rust
fn configure_fixed_timestep(app: &mut App) {
    app.insert_resource(Time::<Fixed>::from_seconds(1.0 / 60.0));

    app.add_systems(FixedUpdate, (
        physics_step,
        apply_forces,
        integrate_positions,
    ));
}

fn physics_step(
    time: Res<Time<Fixed>>,
    mut query: Query<(&mut Transform, &Velocity)>,
) {
    let dt = time.delta_seconds();
    for (mut transform, velocity) in &mut query {
        transform.translation += velocity.0.extend(0.0) * dt;
    }
}
```

## Change Detection

**Reactive Systems**
```rust
// React to component changes
fn on_health_change(
    query: Query<(Entity, &Health), Changed<Health>>,
    mut death_events: EventWriter<DeathEvent>,
) {
    for (entity, health) in &query {
        if health.0 <= 0.0 {
            death_events.send(DeathEvent(entity));
        }
    }
}

// React to component additions
fn on_enemy_spawn(
    query: Query<Entity, Added<Enemy>>,
    mut enemy_count: ResMut<EnemyCount>,
) {
    for entity in &query {
        enemy_count.0 += 1;
        println!("Enemy spawned: {:?}", entity);
    }
}

// Track if component was changed
fn track_changes(
    query: Query<Ref<Transform>, With<Player>>,
) {
    for transform in &query {
        if transform.is_changed() {
            println!("Player moved to: {:?}", transform.translation);
        }
        if transform.is_added() {
            println!("Player spawned!");
        }
    }
}
```

**Deferred Operations**
```rust
// Commands are deferred until the end of the stage
fn spawn_system(mut commands: Commands) {
    let entity = commands.spawn(Enemy).id();
    // Entity exists but components aren't queryable yet
}

// For immediate access, use World directly (exclusive systems)
fn exclusive_spawn(world: &mut World) {
    let entity = world.spawn(Enemy).id();
    // Components are immediately

Related in General