Files
reliquary/plugins/bevy/skills/bevy-0.19/references/ecs.md
T
g4borg c661027d19 🤖 Flesh out bevy-0.19 and bevy-upgrade skills from research
Replace scaffold placeholders with full skill content:
- bevy-0.19: ECS cheat sheet, critical traps table, architecture guide, and 7 reference files
- bevy-upgrade: version migration workflow, transition references for 0.15→0.19, and 3 per-bump reference files
2026-07-07 17:52:10 +02:00

88 lines
4.3 KiB
Markdown

# ECS Core Reference
## Components
`#[derive(Component)]` on structs/enums; requires `Send + Sync + 'static`.
Storage:
- Table (default) — fast iteration.
- `#[component(storage = "SparseSet")]` — fast insert/remove; use for tag-like components that churn.
Attributes:
- `#[component(immutable)]` (0.18+) — only `&T` access, never `&mut T`. Good for identity-like data.
- `#[require(B, C)]` — auto-insert `B` and `C` when this component is added. Constructor forms: `B(value)`, `C { field: val }`, function calls, `= expr`. This replaced `#[derive(Bundle)]` (removed 0.16+); spawn with tuples instead.
- Hooks: `#[component(on_add = f)]`, `on_insert`, `on_replace`, `on_remove`, `on_despawn`. Hook signature: `fn(DeferredWorld, HookContext)`. Hooks run synchronously inside the ECS operation.
- `#[component(clone_behavior = Ignore)]` — skip during entity clone.
## Resources
| Param | Access |
|-------|--------|
| `Res<T>` | shared read |
| `ResMut<T>` | exclusive write |
| `Option<Res<T>>` / `Option<ResMut<T>>` | may not exist (mandatory for render resources in headless-capable systems) |
| `Local<T>` | per-system private state, default-initialized, persists across runs |
| `NonSend<T>` / `NonSendMut<T>` | `!Send` resources (main-thread only) |
World/App: `init_resource::<R>()`, `insert_resource(value)`, `remove_resource::<R>()`.
**0.19**: Resources implement `Component` and live on dedicated entities. Consequences:
- `Query<Entity>` (and other very broad queries) match resource entities — filter with `Without<IsResource>`.
- A type cannot derive both `Component` and `Resource`.
## Systems
Any `fn(params...)` where every param is a `SystemParam`.
- **Fallible** (0.18+): `fn system(...) -> Result` (alias for `Result<(), BevyError>`). Errors are logged; the system runs again next tick. `?` converts anything convertible to `BevyError`. Prefer this over `unwrap()`.
- **Exclusive**: `fn system(world: &mut World)` — full access, blocks parallelism; use sparingly.
Valid `SystemParam` types include: `Query`, `Res`, `ResMut`, `Commands`, `Local`, `MessageReader`, `MessageWriter`, `ParamSet`, `Single`, `Populated`, `SystemName`, `SystemChangeTick`, `RemovedComponents<T>`, `DeferredWorld`, `&World`, `Option<P>`, `Result<P, SystemParamValidationError>`.
- `Single<D, F>` — resolves to exactly one entity or the system doesn't run correctly (panics); the param-level version of `query.single()`.
- `Populated<D, F>` — the system is skipped when the query is empty. Cheap way to avoid per-frame no-op runs.
## Queries
`Query<D, F>``D: QueryData`, `F: QueryFilter` (default `()`).
Data: `&T`, `&mut T`, `Entity`, `Has<T>`, tuples.
Filters:
| Filter | Matches |
|--------|---------|
| `With<T>` / `Without<T>` | presence / absence |
| `Added<T>` | first tick after the component was added |
| `Changed<T>` | first tick after add or mutable deref |
| `Spawned` | first tick after entity spawn |
| `Or<(A, B)>` | either |
Note `Changed<T>` triggers on `&mut` deref even without an actual value change — deref only when writing.
Methods: `.iter()` / `.iter_mut()`, `.get(entity)` / `.get_mut(entity)`, `.single()` (Result-returning; panicking use is a bug magnet), `.par_iter()` / `.par_iter_mut()`.
Reusable shapes: `#[derive(QueryData)]`, `#[derive(QueryFilter)]`.
## Commands
| Call | Effect |
|------|--------|
| `commands.spawn((A, B))` | spawn with component tuple, returns `EntityCommands` |
| `commands.spawn_empty()` | empty entity, chain `.insert(...)` |
| `commands.spawn_batch(iter)` | batch spawn |
| `commands.entity(e).insert(c)` | add/replace component |
| `commands.entity(e).insert_if_new(c)` | add only if absent |
| `commands.entity(e).remove::<T>()` | remove component |
| `commands.entity(e).despawn()` | despawn entity **and descendants** |
| `commands.queue(cmd)` | custom `Command` impl |
Commands are deferred to the next sync point — a spawn is not queryable in the same system.
## Ordering
- `(a, b).chain()` — sequential.
- `.before(x)` / `.after(x)` — explicit constraints against systems or sets.
- `#[derive(SystemSet)]` + `app.configure_sets(Update, SetA.before(SetB))` — named groups; the scalable way to order across plugins.
- Systems with overlapping mutable access and no ordering run nondeterministically; Bevy warns about ambiguities. Order anything where results depend on it.