# Bevy Ecs Systems

> Use when deriving `SystemParam`, grouping with `SystemSet`, gating execution with `.run_if(on_message::<M>())` / `in_state(...)` / `resource_exists::<R>`, ordering with `.before`/`.after`/`.chain()`, or removing systems at runtime with `remove_systems_in_set` (new in 0.18). Covers Bevy 0.18 system params, sets, and run conditions.

- **Type:** Skill
- **Install:** `agentstack add skill-chrisgliddon-bevy-skills-bevy-ecs-systems`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [chrisgliddon](https://agentstack.voostack.com/s/chrisgliddon)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [chrisgliddon](https://github.com/chrisgliddon)
- **Source:** https://github.com/chrisgliddon/bevy-skills/tree/main/skills/bevy-ecs-systems

## Install

```sh
agentstack add skill-chrisgliddon-bevy-skills-bevy-ecs-systems
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Bevy 0.18 — ECS Systems (params, sets, run conditions)

## When to use this skill

- Bundling related state into a single system parameter via `#[derive(SystemParam)]`.
- Grouping systems into a `SystemSet` so callers can order against the whole group.
- Gating systems with run conditions (`run_if`, `on_message`, `in_state`, `resource_exists`, `any_with_component`).
- Hooking into state transitions with `OnEnter(S)` / `OnExit(S)` / `OnTransition { from, to }`.
- Hot-removing systems at runtime (e.g. tearing down a debug overlay) with `remove_systems_in_set`.
- Hitting `ScheduleBuildError` ambiguity panics — the executor no longer guesses.

## Canonical pattern

```rust
use bevy::ecs::system::SystemParam;
use bevy::prelude::*;

#[derive(SystemSet, Hash, PartialEq, Eq, Clone, Debug)]
enum GameLoop { Input, Simulate, Render }

#[derive(States, Default, Hash, PartialEq, Eq, Clone, Debug)]
enum AppState { #[default] Loading, Playing }

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

#[derive(Message)]
struct GoalScored { team: u8 }

// Composite param: pass one argument, get four.
// 'w = world borrow; 's = system-local state borrow.
#[derive(SystemParam)]
struct GameCtx {
    time:  Res,
    score: ResMut,
    goals: MessageReader,
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .init_resource::()
        .add_message::()
        .init_state::()
        .configure_sets(Update, (GameLoop::Input, GameLoop::Simulate, GameLoop::Render).chain())
        // State schedule: fires once when entering Playing.
        .add_systems(OnEnter(AppState::Playing), spawn_level)
        // State schedule: fires once when leaving Playing.
        .add_systems(OnExit(AppState::Playing), despawn_level)
        .add_systems(Update, read_input.in_set(GameLoop::Input))
        .add_systems(
            Update,
            tally_goals
                .in_set(GameLoop::Simulate)
                .run_if(on_message::),
        )
        .add_systems(Update, draw_hud.in_set(GameLoop::Render))
        .run();
}

#[derive(Component)]
struct LevelEntity;

fn spawn_level(mut commands: Commands) {
    commands.spawn((LevelEntity, Name::new("level")));
}

fn despawn_level(mut commands: Commands, query: Query>) {
    for e in &query { commands.entity(e).despawn(); }
}

fn read_input(mut writer: MessageWriter) {
    writer.write(GoalScored { team: 0 });
}

fn tally_goals(mut ctx: GameCtx) {
    let _ = ctx.time.delta_secs();
    for goal in ctx.goals.read() {
        ctx.score.0 += 1;
        info!("team {} scored, total {}", goal.team, ctx.score.0);
    }
}

fn draw_hud(score: Res) { let _ = score.0; }
```

## Run condition cheat sheet

| Built-in | Triggers when |
|---|---|
| `on_message::` | The `Messages` buffer has unread items. |
| `resource_exists::` | Resource `R` is in the `World`. |
| `resource_changed::` | `R` was mutated this tick. |
| `in_state(MyState::X)` | Current `State` equals `X`. |
| `state_changed::` | The state changed this tick. |
| `any_with_component::` | At least one entity has `C`. |

Combine with `.and()` / `.or()`: `run_if(in_state(GameState::Playing).and(resource_exists::()))`.

## Topics

| Topic | Reference |
|---|---|
| `#[derive(SystemParam)]`, `'w`/`'s` lifetimes, `Local`, `Commands` deferred apply | [references/system-params.md](references/system-params.md) |
| `#[derive(SystemSet)]` requirements, `.in_set`, `.configure_sets`, `.chain()` | [references/system-sets.md](references/system-sets.md) |
| Every built-in condition, `.and()`/`.or()`/`not()`, custom conditions, cost rule | [references/run-conditions.md](references/run-conditions.md) |
| `OnEnter(S)` / `OnExit(S)` / `OnTransition`, `NextState`, `set_if_neq` | [references/state-schedules.md](references/state-schedules.md) |
| `.before`, `.after`, `.chain()`, `.ambiguous_with`, debugging ambiguity errors | [references/ordering.md](references/ordering.md) |
| `remove_systems_in_set`, `ScheduleCleanupPolicy`, 3-arg vs 4-arg receivers, side-effect limits | [references/runtime-removal.md](references/runtime-removal.md) |

## Gotchas (0.18)

- **`SimpleExecutor` is gone.** Any ambiguity between systems sharing data is now a build-time error. Fix with `.before`, `.after`, `.chain()`, or `.ambiguous_with(other)` (explicit accept). See [references/ordering.md](references/ordering.md).
- **`MessageReader` / `MessageWriter`, not `EventReader` / `EventWriter`.** Renamed in 0.17. Trait derive is `#[derive(Message)]`; registrar is `app.add_message::()`.
- **`next_state.set(S)` always fires `OnExit`/`OnEnter` in 0.18.** Use `set_if_neq` for the old behaviour. See [references/state-schedules.md](references/state-schedules.md).
- **`Schedules::remove_systems_in_set` takes 4 args in 0.18** (`schedule_label`, `set`, `world`, `ScheduleCleanupPolicy`); `Schedule::remove_systems_in_set` takes 3. See [references/runtime-removal.md](references/runtime-removal.md).

## See also

- `bevy-core-concepts` — which schedule (`Startup`, `Update`, `FixedUpdate`) to add a system to.
- `bevy-ecs-queries` — query is one of many `SystemParam`s; `Changed` / `Added` filters.
- `bevy-ecs-components` — defining the `Component` types your systems act on.
- `bevy-migration-0-17-to-0-18` — full Message/Event rename, `MaterialPlugin` changes.

## Source & license

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

- **Author:** [chrisgliddon](https://github.com/chrisgliddon)
- **Source:** [chrisgliddon/bevy-skills](https://github.com/chrisgliddon/bevy-skills)
- **License:** MIT

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

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-chrisgliddon-bevy-skills-bevy-ecs-systems
- Seller: https://agentstack.voostack.com/s/chrisgliddon
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
