Open-Source Wikis

/

Bevy

/

Packages

/

bevy_ecs

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.rs

Key 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. Uses bevy_tasks::ComputeTaskPool and runs systems concurrently when their access doesn't conflict.
  • SingleThreadedExecutor (single_threaded.rs) — fallback for multi_threaded = false or 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 is Table in table.rs; sparse-set storage is in sparse_set.rs.
  • Want to change scheduling semantics? crates/bevy_ecs/src/schedule/. The graph builder is in schedule.rs; the executors are in executor/.
  • Want a new system parameter? Implement SystemParam (and ReadOnlySystemParam if applicable) in your crate. See Local, Res, Query, Commands for templates.
  • Want a new query filter? Implement QueryFilter. Added, Changed, With, Without are in crates/bevy_ecs/src/query/filter.rs.
  • Want a new lifecycle event? Add to crates/bevy_ecs/src/lifecycle.rs. Hook plumbing is in crates/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 in crates/bevy_ecs/src/reflect/.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

bevy_ecs – Bevy wiki | Factory