ai-behavior-trees-util…
Engine-neutral behavior tree and utility AI code for NPCs in any language: Blackboard, reactive composites, decorators and…
Builds and debugs Bevy 0.20 games in Rust: components, Query and Commands systems, schedules, States, observers and headless tests. Use when Cargo.toml depends on bevy, on a B0001 query-conflict panic, or pre-0.20 code (EventReader, SpriteBundle). Not for engine-neutral AI,
Claude Code
$ npx -y skills add gamedev-skills/awesome-gamedev-agent-skills --skill bevy-ecs --agent claude-code
Codex
$ npx skills add gamedev-skills/awesome-gamedev-agent-skills --skill bevy-ecs -a codex -y
Cursor
$ npx skills add gamedev-skills/awesome-gamedev-agent-skills --skill bevy-ecs -a cursor -y
Gemini CLI
$ npx skills add gamedev-skills/awesome-gamedev-agent-skills --skill bevy-ecs -a gemini-cli -y
GitHub Copilot
$ npx skills add gamedev-skills/awesome-gamedev-agent-skills --skill bevy-ecs -a github-copilot -y
OpenCode
$ npx skills add gamedev-skills/awesome-gamedev-agent-skills --skill bevy-ecs -a opencode -y
Windsurf
$ npx skills add gamedev-skills/awesome-gamedev-agent-skills --skill bevy-ecs -a windsurf -y
name: bevy-ecs description: > Builds and debugs Bevy 0.20 games in Rust: components, Query and Commands systems, schedules, States, observers and headless tests. Use when Cargo.toml depends on bevy, on a B0001 query-conflict panic, or pre-0.20 code (EventReader, SpriteBundle). Not for engine-neutral AI, generation, physics or shaders (`game-ai`, `procedural-gen`, `physics-tuning`, `shader-programming`).
Structure and debug a Bevy game around its ECS. New code targets **Bevy 0.20** (`bevy = "0.20"`, which resolves to the latest 0.20.x patch). Bevy breaks APIs in every minor release, so the version the project pins decides which syntax is right.
gating them on state or timers, or passing data with messages and observers.
an older Bevy release.
**When not to use:** rendering pipelines, custom shaders, UI layout and audio are outside this skill; AI and generation algorithms go to `game-ai` / `procedural-gen`.
1. **Read the pinned version first.** `Cargo.toml` gives the requirement and `Cargo.lock` the resolved `bevy` version. Keep it; a minor-release migration is a separate task that follows that release's migration guide. Never mix snippets from two minors. 2. **Model the data.** Per-entity data is a `#[derive(Component)]`; one-of-a-kind data (score, settings) is a `#[derive(Resource)]`. Spawn entities as tuples of components; required components fill in the rest (`commands.spawn(Camera2d)` adds `Transform`, `Camera` and friends). 3. **Write systems as plain functions** whose parameters declare access: `Query<D, F>`, `Res`/`ResMut`, `Commands`, `MessageReader`/`MessageWriter`, `Single`. Systems with non-conflicting access run in parallel, in no guaranteed order. 4. **Order only what must be ordered** (`.chain()`, `.before`/`.after`, `SystemSet`s) and gate with `run_if`. Group features into `Plugin`s. Scale motion by `time.delta_secs()` or put it in `FixedUpdate`. 5. **Verify.** Run `cargo check`, then `cargo test` with a headless `App` test (pattern below) for each rule you changed, then `cargo run` and read the first panic: Bevy reports conflicting system parameters when a schedule first runs, with an error code and a link. Report which of these you ran; a clean `cargo check` proves only that it compiles. When the change is visual, ask the user to run the game and report what they see.
#[derive(Component)]
struct Player;
#[derive(Component)]
struct Velocity(Vec2);
fn setup(mut commands: Commands) {
commands.spawn(Camera2d);
commands.spawn((Player, Velocity(Vec2::new(150.0, 0.0)), Transform::default()));
}
fn move_players(time: Res<Time>, mut q: Query<(&Velocity, &mut Transform)>) {
for (v, mut t) in &mut q {
t.translation += v.0.extend(0.0) * time.delta_secs();
}
}
// Two &mut Transform queries in one system must be provably disjoint.
fn separate(
mut players: Query<&mut Transform, With<Player>>,
mut others: Query<&mut Transform, Without<Player>>,
) { /* ... */ }struct GameplayPlugin;
impl Plugin for GameplayPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(Score(0))
.add_systems(Startup, setup)
// damage must resolve before deaths are checked
.add_systems(Update, (apply_damage, check_deaths).chain())
.add_systems(Update, spawn_wave.run_if(in_state(AppState::Playing)));
}
}`MinimalPlugins` gives time and a schedule runner with no window or GPU, so this runs under plain `cargo test`. Drive time by hand to make movement deterministic.
#[cfg(test)]
mod tests {
use super::*;
use bevy::time::TimeUpdateStrategy;
use std::time::Duration;
#[test]
fn player_moves_at_its_velocity() {
let mut app = App::new();
app.add_plugins(MinimalPlugins)
.insert_resource(TimeUpdateStrategy::ManualDuration(Duration::from_millis(100)))
.add_systems(Update, move_players);
let id = app.world_mut().spawn((Velocity(Vec2::X * 10.0), Transform::default())).id();
for _ in 0..5 {
app.update();
}
// The first update has a zero delta, so 5 updates advance 4 x 0.1 s.
let x = app.world().get::<Transform>(id).unwrap().translation.x;
assert!((x - 4.0).abs() < 1e-4, "x = {x}");
}
}For whole-world assertions use `app.world_mut().query_filtered::<&Health, With<Player>>()` and `.single(world)`. States need `bevy::state::app::StatesPlugin` next to `MinimalPlugins`.
that conflicts with a previous system parameter`** → two queries in one system can both reach the same component mutably. Make them disjoint with `With`/`Without`, or use `ParamSet`.
panics** → since 0.19 every resource lives on an entity tagged `IsResource`, so broad queries match resources too. The error is `B0002` when the `Res` comes after the query and `B0001` when it comes first. Query your own marker, or add `Without<IsResource>` (`use bevy::ecs::resource::IsResource`).
`Component` on one type → since 0.19 `Resource` implies `Component`; derive `Resource` alone.
deferred to the next sync point. Read it in a later system, or order the reader after the spawner.
<img src="docs/assets/awesome-gamedev-agent-skills-banner.png" width="820" alt="awesome-gamedev-agent-skills — game-dev skills for AI coding agents.
Engine-neutral behavior tree and utility AI code for NPCs in any language: Blackboard, reactive composites, decorators and…
Engine-neutral game audio technique: bus layout, volume sliders in decibels, loudness, ducking music under dialogue, adaptive…
Builds and tunes game cameras, engine-neutral with Godot and Cinemachine 3 mappings: 2D follow with deadzone and look-ahead,…
Plans, generates or sources, and validates consistent game art: style guides, sprites, pixel art, tilesets, icons and textures,…
Builds engine-neutral branching dialogue with Ink, Yarn Spinner or a small JSON graph runner: lines, choices, conditions,…
Designs engine-neutral enemy and NPC AI: picks FSM, behavior tree, utility or GOAP, and builds perception, steering and A*…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.