# Bevy Skill Research: ECS Core > **Bevy version**: 0.18, with 0.19 notes > **Date**: 2026-07-07 ## Components **Derive**: `#[derive(Component)]` on structs/enums. Requires `Send + Sync + 'static`. **Storage**: - Table (default) — fast iteration - `#[component(storage = "SparseSet")]` — fast insert/remove **Immutable components** (0.18+): `#[component(immutable)]` — restricts to `&T` only, no `&mut T`. **Required components**: `#[require(B, C)]` — auto-inserts `B` and `C` when this component is added. Supports constructors: `B(value)`, `C { field: val }`, function calls, `= expr`. **Hooks**: `on_add`, `on_insert`, `on_replace`, `on_remove`, `on_despawn` — set via `#[component(on_add = fn_name)]` or self-referential `#[component(on_add)]`. **Clone behavior**: `#[component(clone_behavior = Ignore)]` to skip during entity clone. **Bundles removed** (0.16+): Use `#[require(...)]` or spawn with tuples instead of `#[derive(Bundle)]`. ## Resources | Type | Purpose | |------|---------| | `Res` | Shared read access | | `ResMut` | Exclusive write access | | `Option>` / `Option>` | Resources that may not exist | | `Local<'a, T>` | Per-system private state, persists across calls, default-initialized | | `NonSend` / `NonSendMut` | For `!Send` resources | World methods: `init_resource::()` (uses `Default`), `insert_resource(value)`, `remove_resource::()`. **0.19 change**: Resources implement `Component` and live on abstract entities. `Query` now matches resource entities — filter with `Without`. Cannot derive both `Component` and `Resource` on one type. ## Systems **Standard**: any `fn(params...) { }` where each param implements `SystemParam`. **Fallible** (0.18+): `fn system(params...) -> Result` — bare `Result` (alias for `Result<(), BevyError>`). Errors are logged, system continues on next tick. `?` operator works with any error convertible to `BevyError`. **Exclusive**: `fn system(world: &mut World) { }` — full world access, blocks parallelism. **Registration**: `app.add_systems(Update, (sys_a, sys_b))` or `app.add_systems(Startup, setup)`. ### Valid SystemParam Types `Query`, `Res`, `ResMut`, `Commands`, `Local`, `MessageReader`, `MessageWriter`, `ParamSet`, `Single`, `Populated`, `SystemName`, `SystemChangeTick`, `RemovedComponents`, `DeferredWorld`, `&World`, `()`, `Option`, `Result`, `PhantomData`. ## Queries **Signature**: `Query` where `D: QueryData`, `F: QueryFilter` (default `()`). ### Data Parameters - `&T` — read - `&mut T` — write - `Entity` — entity ID - `Has` — returns bool - Tuples of the above ### Filters | Filter | Fires when | |--------|-----------| | `With` | Entity has component T | | `Without` | Entity lacks component T | | `Added` | First tick after component added | | `Changed` | First tick after add or mut deref | | `Spawned` | First tick after entity spawn (new) | | `Or<(A, B)>` | Either filter matches | ### Key Methods - `.iter()` / `.iter_mut()` — iterate all matching - `.get(entity)` / `.get_mut(entity)` — single entity lookup - `.single()` — panics if not exactly one match - `.par_iter()` / `.par_iter_mut()` — parallel iteration ### Specialized System Params - `Single` — system param alternative to `query.single()`, panics if not exactly one - `Populated` — system skips execution if query is empty ### Custom Derives - `#[derive(QueryData)]` — for complex query data - `#[derive(QueryFilter)]` — for reusable filter combinations ## Commands | Method | Purpose | |--------|---------| | `commands.spawn((CompA, CompB))` | Spawn entity with tuple, returns `EntityCommands` | | `commands.spawn_empty()` | Empty entity, chain `.insert(bundle)` | | `commands.spawn_batch(iter)` | Batch spawn from iterator | | `commands.entity(e).insert(comp)` | Add component to existing entity | | `commands.entity(e).insert_if_new(comp)` | Add only if absent | | `commands.entity(e).remove::()` | Remove component | | `commands.entity(e).despawn()` | Destroy entity (auto-recursive with children) | | `commands.queue(custom_command)` | Push custom `Command` impl | ## System Ordering - `(a, b).chain()` — `a` runs before `b` - `.before(system_or_set)` / `.after(system_or_set)` — explicit ordering - `#[derive(SystemSet)]` — define named sets for grouping - `app.configure_sets(Update, SetA.before(SetB))` — order sets - `AmbiguousSystemConflictsWarning` warns about unordered systems with overlapping access