Skip to content
Development
Skill

/bevy-ecs

Structure a Bevy app around its Entity Component System: build the App with plugins, define Component/Resource types, write systems with Query/Res/Commands, filter and order systems, and use the Time resource for frame-rate-independent motion. Use when building or debugging a

From plugin
awesome-gamedev-agent-skills
45567 skills
Install
$ npx -y skills add gamedev-skills/awesome-gamedev-agent-skills --skill bevy-ecs --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/bevy-ecs

Context preview

The summary Claude sees to decide when to auto-load this skill.

Structure a Bevy app around its Entity Component System: build the App with plugins, define Component/Resource types, write systems with Query/Res/Commands, filter and order systems, and use the Time resource for frame-rate-independent motion. Use when building or debugging a

SKILL.md

bevy-ecs.SKILL.md
name: bevy-ecs
description: >
  Structure a Bevy app around its Entity Component System: build the App with
  plugins, define Component/Resource types, write systems with Query/Res/Commands,
  filter and order systems, and use the Time resource for frame-rate-independent
  motion. Use when building or debugging a Bevy game in Rust — when the user
  mentions Bevy, ECS, App::new, add_systems, Query, Commands, components/systems,
  or a Cargo.toml depending on bevy.

Bevy ECS

Structure a Bevy game in Rust around the Entity Component System: the `App` and plugins, components and resources, systems with queries, scheduling, and frame-rate-independent updates. New examples target **Bevy 0.19**. If the project already pins another release, keep that release and use its matching migration guide.

When to use

  • Use when wiring a Bevy `App`, defining `Component`/`Resource` types, writing

systems that query entities, ordering/filtering systems, or fixing borrow-conflict panics and frame-dependent movement.

  • Use when `Cargo.toml` depends on `bevy` and code calls `App::new()`,

`add_systems`, `Query`, or `Commands`.

**When *not* to use:** this is the ECS core. Deep rendering, custom shaders/ pipelines, UI layout, and audio are separate concerns. For engine-agnostic AI or procedural algorithms, pair with `game-ai` / `procedural-gen`.

Core workflow

1. **Detect and pin the version.** Read `Cargo.toml` and `Cargo.lock` first. For a new project use `bevy = "0.19"`; never silently migrate an existing project across a Bevy minor release. Treat the matching docs and migration guides as truth. 2. **Build the `App`.** `App::new().add_plugins(DefaultPlugins)` gives windowing, input, rendering, time, etc. Register systems into schedules: `Startup` (once) and `Update` (every frame). 3. **Model data as components, globals as resources.** `#[derive(Component)]` for per-entity data; `#[derive(Resource)]` for one-of-a-kind data (score, settings, the `Time` clock). In 0.19 `Resource` extends `Component`, so do not derive both. 4. **Write systems as plain functions.** Parameters declare data access: `Query<...>` for entities, `Res<T>`/`ResMut<T>` for resources, `Commands` for deferred spawn/despawn. Systems run in parallel when their accesses don't conflict. 5. **Drive motion by `time.delta_secs()`** so speed is frame-rate independent. 6. **Order only what must be ordered** with `.chain()` or explicit constraints; gate systems with `run_if`. Group related setup into `Plugin`s. Build with `cargo run` and read the panics — Bevy reports conflicting queries at startup.

Patterns

1. Cargo.toml + minimal App

# Cargo.toml — pin the version; the API differs across minor releases.
[dependencies]
bevy = "0.19"
// main.rs
use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)            // window, input, render, time, ...
        .add_systems(Startup, setup)            // runs once at startup
        .add_systems(Update, move_players)      // runs every frame
        .run();
}

2. Components, resources, and spawning

#[derive(Component)]
struct Player;

#[derive(Component)]
struct Velocity(Vec2);

#[derive(Resource)]
struct Score(u32);

fn setup(mut commands: Commands) {
    commands.insert_resource(Score(0));

    // Camera2d is a component with required components (bundles removed in 0.16);
    // spawning it pulls in Transform, Camera, etc. automatically.
    commands.spawn(Camera2d);

    // Spawn an entity as a tuple of components.
    commands.spawn((
        Player,
        Velocity(Vec2::new(150.0, 0.0)),
        Transform::from_xyz(0.0, 0.0, 0.0),
    ));
}

3. A system with a query + the Time resource

// Iterate every entity that has BOTH Velocity and Transform; mutate Transform.
fn move_players(time: Res<Time>, mut query: Query<(&Velocity, &mut Transform)>) {
    for (velocity, mut transform) in &mut query {
        // delta_secs() is f32 seconds (renamed from delta_seconds() in 0.16).
        transform.translation += velocity.0.extend(0.0) * time.delta_secs();
    }
}

4. Query filters (With / Without / Changed)

// Only entities tagged Player (the Player component itself isn't read).
fn aim_player(mut q: Query<&mut Transform, With<Player>>) { /* ... */ }

// Disjoint two mutable Transform queries so they don't conflict at runtime.
fn separate(
    mut players: Query<&mut Transform, With<Player>>,
    mut enemies: Query<&mut Transform, Without<Player>>,
) { /* ... */ }

// React only when Health changed since last run (change detection).
fn on_health_change(q: Query<&Health, Changed<Health>>) {
    for health in &q { /* update the HUD, etc. */ }
}

5. Resources: read and write

fn add_points(mut score: ResMut<Score>) {
    score.0 += 10;                 // ResMut = write access
}

fn show_score(score: Res<Score>) {
    info!("score: {}", score.0);   // Res = read access
}

6. Ordering, run conditions, and plugins

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, GameplayPlugin))
        // .chain() forces order: damage resolves before death is checked.
        .add_systems(Update, (apply_damage, check_deaths).chain())
        // run_if gates a system on a condition each frame.
        .add_systems(Update, spawn_wave.run_if(wave_timer_finished))
        .run();
}

struct GameplayPlugin;
impl Plugin for GameplayPlugin {
    fn build(&self, app: &mut App) {
        app.insert_resource(Score(0))
           .add_systems(Startup, setup)
           .add_systems(Update, (move_players, add_points));
    }
}

Pitfalls

  • **`delta_seconds()` not found** → it was renamed to `time.delta_secs()` (and

`elapsed_secs()`) in 0.16. Using the old name fails to compile.

  • **Movement speed scales with frame rate** → multiply per-frame changes by

`time.delta_secs()`. Never assume a fixed frame time.

  • **Panic: "confli
Read more
Ships withawesome-gamedev-agent-skills

<img src="docs/assets/banner.png" width="820" alt="awesome-gamedev-agent-skills — game-dev skills for AI coding agents.

Get the whole plugin
Stats
458
Stars
36
Forks
Active
Maintenance
Python
Language
Apache-2.0
License
1d ago
Last commit
1mo ago
Created

Repo: gamedev-skills/awesome-gamedev-agent-skills