bevyengine/bevy
bevy_ecs
The Entity Component System. The largest crate in the workspace (~150 source files), the core data model for everything else, and the source of Bevy's parallel-by-default scheduling.
Purpose
bevy_ecs defines:
- Entities, components, resources, and bundles — the data model.
- Worlds — the container for all of the above.
- Queries — typed iteration over entities.
- Systems — functions that read and write the world, with their access pattern declared in their parameter types.
- Schedules — directed graphs of systems with ordering, run conditions, and parallel execution.
- Observers and lifecycle hooks — reactive callbacks for component add/remove/replace.
- Relationships — first-class entity-to-entity links (parent/child, etc.).
- Change detection — per-component "this changed this tick" flags.
The crate is no_std-friendly and forbids most of std in its core API surface. The exception is the multi-threaded executor, which requires threading.
Directory layout
crates/bevy_ecs/src/
├── archetype.rs # Archetype indexing (storage layout)
├── batching.rs # Parallel iteration helpers
├── bundle/ # Bundle trait, derive, dynamic bundles
├── change_detection/ # Mut/Ref, tick tracking
├── component/ # Component trait, hooks, registration
├── entity/ # Entity ID, allocator, sparse storage
├── entity_disabling.rs # Marker-based disable/enable
├── error/ # Per-system error reporting
├── event/ # ECS event types (older — observers prefer)
├── hierarchy.rs # ChildOf / Children relationship
├── lifecycle.rs # Add/Insert/Remove/Despawn lifecycle types
├── message/ # ECS messaging system
├── observer/ # Observers (reactive event handlers)
├── query/ # Query, QueryState, query iteration
├── reflect/ # Reflection bridge
├── relationship/ # Generic relationship machinery
├── resource.rs # Resource trait
├── schedule/ # Schedules, SystemSet, executors
├── spawn.rs # Spawn helpers
├── storage/ # Table + sparse-set storage backends
├── system/ # System trait, params, commands, registry
├── template.rs # Reflectable templates (used by BSN)
├── world/ # World container, EntityRef, EntityMut
└── lib.rsKey abstractions
| Type | File | One-line description |
|---|---|---|
World |
crates/bevy_ecs/src/world/mod.rs |
The owner of all entities, components, resources, and schedules. |
Entity |
crates/bevy_ecs/src/entity/mod.rs |
An opaque ID with index + generation. |
Component |
crates/bevy_ecs/src/component/mod.rs |
Trait — #[derive(Component)]. |
Resource |
crates/bevy_ecs/src/resource.rs |
Trait — singleton on the world. |
Bundle |
crates/bevy_ecs/src/bundle/mod.rs |
Trait for a typed group of components. |
Query<D, F> |
crates/bevy_ecs/src/query/mod.rs |
The system parameter for entity iteration. |
Commands |
crates/bevy_ecs/src/system/commands/mod.rs |
Deferred world mutation buffer. |
IntoSystem / System |
crates/bevy_ecs/src/system/system.rs |
The system abstraction itself. |
Schedule |
crates/bevy_ecs/src/schedule/schedule.rs |
A graph of systems with ordering and conditions. |
SystemSet |
crates/bevy_ecs/src/schedule/set.rs |
Label for grouping systems. |
Observer |
crates/bevy_ecs/src/observer/mod.rs |
Reactive function fired on lifecycle / custom events. |
Mut<T> / Ref<T> |
crates/bevy_ecs/src/change_detection/ |
Smart references that record reads/writes for change detection. |
Archetype |
crates/bevy_ecs/src/archetype.rs |
The unique component-set identity used for storage indexing. |
How it works
Storage
Every entity belongs to one archetype — the set of components it has. Components with the default storage live in tables (a Struct-of-Arrays layout, one column per component, one row per entity). Components declared with #[component(storage = "SparseSet")] live in a sparse set keyed by entity, optimized for components that get added and removed often.
When you add or remove a component the entity migrates to a new archetype and the table data is moved row-by-row. The set of archetypes is built on demand and indexed by component-id bitset.
graph LR
Entity --> Location[ArchetypeId + row]
Location --> Archetype
Archetype --> Tables
Tables --> ColumnA[Column<Pos>]
Tables --> ColumnB[Column<Vel>]Queries
A Query<D, F> is parameterized by the data it fetches and a filter. The query is precomputed: at system-register time, the schedule walks all archetypes and records which ones match. Iteration just walks matched archetype tables in order — no runtime archetype filtering on each frame.
QueryState is the cached lookup. Most users never see it directly; Query is the system parameter that wraps it.
Systems and parameters
A system is anything that implements System. The blanket impl<F: SystemParamFunction> IntoSystem<...> for F lets you use plain functions. Each parameter type implements SystemParam, which declares:
- What data the system needs.
- What access pattern (read, write, world-exclusive) it requires.
- How to fetch the value from a world.
This is what makes parallelism free: the executor can compute conflicts from the parameter types alone.
fn move_things(
time: Res<Time>,
mut q: Query<(&mut Transform, &Velocity)>,
) {
for (mut t, v) in &mut q {
t.translation += v.0 * time.delta_secs();
}
}Res<Time> declares a read of the Time resource; Query<(&mut Transform, &Velocity)> declares mutable access to Transform and read access to Velocity for matched archetypes. Two systems can run in parallel iff their declared accesses do not conflict.
Schedules and executors
A Schedule is a graph: nodes are systems and system sets, edges are before/after constraints, and each node has a list of run conditions. Building a schedule produces an executable plan: the topological sort + a conflict graph.
Two executors live under crates/bevy_ecs/src/schedule/executor/:
MultiThreadedExecutor(multi_threaded.rs) — the default. Usesbevy_tasks::ComputeTaskPooland runs systems concurrently when their access doesn't conflict.SingleThreadedExecutor(single_threaded.rs) — fallback formulti_threaded = falseor platforms without threads (Wasm without atomics).
Both honor the same Schedule graph; they only differ in how systems get dispatched.
Observers
An observer is a function that runs in response to an event:
world.observe(|trigger: Trigger<OnAdd, Player>, mut commands: Commands| {
commands.entity(trigger.entity()).insert(Health(100));
});The built-in events are OnAdd, OnInsert, OnRemove, OnReplace, OnDespawn. Custom events implement Event. Observers are the modern pattern for ECS reactivity; the older EventReader<T>/EventWriter<T> system is still supported for cases where you need batch processing.
Hooks
Hooks are simpler than observers — they are direct function pointers attached to a component at registration time. They run inline, synchronously, when the component is added or removed. Use them when you can't tolerate the deferred-by-default semantics of observers.
Change detection
Every &mut T access in a query goes through Mut<T>, which sets a per-component "changed this tick" flag on write. Filters like Changed<T> or Added<T> use those flags. The implementation is in crates/bevy_ecs/src/change_detection/.
A "tick" is a u32 that increments once per Schedule::run. Per-component last-changed and last-added ticks are stored alongside component data in the tables.
Integration points
- Depends on:
bevy_ptr,bevy_reflect(optional),bevy_tasks,bevy_macros_utils,bevy_platform. - Depended on by: Every other Bevy engine crate. The ECS is the data substrate.
- Macros:
bevy_ecs_macros(in-repo) provides#[derive(Component)],#[derive(Bundle)],#[derive(SystemSet)],#[derive(Resource)].
Entry points for modification
- Want to optimize storage?
crates/bevy_ecs/src/storage/. The table type isTableintable.rs; sparse-set storage is insparse_set.rs. - Want to change scheduling semantics?
crates/bevy_ecs/src/schedule/. The graph builder is inschedule.rs; the executors are inexecutor/. - Want a new system parameter? Implement
SystemParam(andReadOnlySystemParamif applicable) in your crate. SeeLocal,Res,Query,Commandsfor templates. - Want a new query filter? Implement
QueryFilter.Added,Changed,With,Withoutare incrates/bevy_ecs/src/query/filter.rs. - Want a new lifecycle event? Add to
crates/bevy_ecs/src/lifecycle.rs. Hook plumbing is incrates/bevy_ecs/src/component/hooks.rs.
See also
- Primitives — pages on
World,Entity,Component,System,Schedule,Resource. - Architecture — the App lifecycle and SubApp boundary.
bevy_app— the schedule labels and plugin system that drive this crate.bevy_reflect— the reflection bridge incrates/bevy_ecs/src/reflect/.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.