AgentStack
SKILL verified Apache-2.0 Self-run

Bevy Ecs

skill-gamedev-skills-awesome-gamedev-agent-skills-bevy-ecs · by gamedev-skills

>

No reviews yet
0 installs
14 views
0.0% view→install

Install

$ agentstack add skill-gamedev-skills-awesome-gamedev-agent-skills-bevy-ecs

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution Used
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Bevy Ecs? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

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. Pins Bevy 0.16+ (code targets 0.16; Bevy's API shifts each minor release — match your Cargo.toml).

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. Pin the version. Bevy's API changes every minor release. Set

bevy = "0.16" (or your target) in Cargo.toml and treat the matching docs as truth. Enable dynamic_linking in dev for faster iterative builds.

  1. Build the App. App::new().add_plugins(DefaultPlugins) gives windowing,

input, rendering, time, etc. Register systems into schedules: Startup (once) and Update (every frame).

  1. 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).

  1. Write systems as plain functions. Parameters declare data access: Query

for entities, Res/ResMut for resources, Commands for deferred spawn/despawn. Systems run in parallel when their accesses don't conflict.

  1. Drive motion by time.delta_secs() so speed is frame-rate independent.
  2. Order only what must be ordered with .chain() or explicit constraints;

gate systems with run_if. Group related setup into Plugins. 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.16"
# Dev-only: faster recompiles. (Add the matching dynamic linking setup per the book.)
# bevy = { version = "0.16", features = ["dynamic_linking"] }
// 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, mut query: Query) {
    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>) { /* ... */ }

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

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

5. Resources: read and write

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

fn show_score(score: Res) {
    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: "conflicting accesses" / "&mut T and &mut T" → two Querys in one

system both write the same component, or one reads while another writes overlapping entities. Make them disjoint with With/Without, or use ParamSet.

  • Camera2dBundle/SpriteBundle not found → bundles were deprecated in 0.15 and

removed in 0.16. Spawn the components directly (Camera2d, Sprite, Transform); required components fill in the rest.

  • "trait Component is not implemented" → you forgot #[derive(Component)]

(or #[derive(Resource)] for a resource).

  • Spawned entity not visible to a later query in the same frameCommands are

deferred and applied at the next sync point. Read the entity in a subsequent system, not the one that spawned it.

  • System order assumed but not enforced → systems run in parallel by default.

If B must follow A, add (A, B).chain() or an explicit ordering constraint.

  • Copy-pasting older Bevy snippets → APIs shift between minor versions (e.g.

the buffered-event API was reworked after 0.16). Verify against the docs for your pinned version; don't mix versions.

References

  • For schedules and SystemSet ordering, States/OnEnter/OnExit, change

detection, Commands lifecycle and sync points, ParamSet for conflicting queries, and a version note on the events/observers API, read references/queries-and-scheduling.md.

Related skills

  • game-ai — FSMs/behavior trees/steering as portable concepts to implement in ECS.
  • procedural-gen — noise/RNG/generation algorithms to drive from systems.
  • pygame-core / love2d-core — lighter-weight engines for smaller projects.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.