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

4.3 KiB

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.