How Does Bevy Engine's ECS (Entity Component System) Work?

Updated

Bevy's ECS is the architectural core of the entire engine: all engine and game logic runs through it. If you're evaluating Bevy or learning it, the key idea is that data lives in components (plain Rust structs), behavior lives in systems (plain Rust functions), and entities are just IDs that tie components together. This explainer covers how those pieces fit, what the scheduler does, and what it means for how you write game code.

The three building blocks

Entities

An entity is an identifier, not an object. It has no fields and no methods of its own — it exists only to associate a set of components with one another. A player might be an entity with a Position, Velocity, and Health component attached.

Components

Components are Rust structs. That's the whole definition — no base class to inherit from, no registration boilerplate required beyond what Bevy's derive macros handle. Because they're ordinary structs, you can use Rust's type system, traits, and ownership rules directly on your game data.

Systems

Systems are Rust functions. They read and write components and resources, and Bevy decides when and where they run. Because a system is just a function, it's testable and composable in the same way any other Rust function is.

How data is accessed: queries and resources

Systems don't reach into a global object graph. They declare what they need:

  • Queries select entities by the components they have. A movement system might query all entities with both Position and Velocity, then update one from the other.
  • Global resources hold singleton data shared across systems — things like a score, a game state enum, or an input map.
  • Local resources are per-system state that persists between runs of that same system, useful for things like a timer or a cached value that only that system cares about.
  • Change detection lets a system ask whether a component or resource was modified since it last ran, so you can avoid redundant work instead of recomputing everything every frame.

The scheduler: parallel by default

Bevy's scheduler is described as lock-free and parallel. The practical consequence is that systems which don't conflict — one reading Position while another writes Score — can run at the same time without you writing threading code. You express what each system touches through its parameters, and the scheduler uses that to decide what can safely run concurrently.

This is why the ECS is framed as both fast (massively parallel and cache-friendly, since components of the same type are stored together) and simple (components are structs, systems are functions). The performance characteristics come from the data layout and scheduler, not from anything you have to hand-optimize.

What this changes about how you write code

Traditional object-oriented approach Bevy ECS approach
Behavior lives on the object that owns the data Behavior lives in systems; data lives in components
Inheritance hierarchies for shared behavior Composition: attach the components an entity needs
You manage update order and threading manually The scheduler derives order and parallelism from system access
Adding a feature often means touching a class Adding a feature often means adding a component and a system

A concrete example: to make entities blink, you add a Blink component to the entities that should blink and write one system that queries Blink and toggles visibility. You don't modify a Sprite class or subclass anything — the behavior is opt-in per entity by attaching the component.

Where the ECS shows up beyond gameplay

The ECS isn't confined to game logic. Bevy's UI framework is described as ECS-driven and built directly on top of Bevy's ECS, renderer, and scene plugins. Scenes create, save, and load ECS worlds, and loading a scene preserves entity IDs — which the documentation notes is useful for save games. Animation is also ECS-based: skeletal rig animation is driven by an ECS-based joint API.

That consistency matters if you're deciding whether to invest in learning the model: the same mental framework applies to rendering, UI, scenes, and animation, not just to your own gameplay systems.

Choosing Bevy with the ECS in mind

Bevy is a data-driven engine built in Rust, described as free and open source. The ECS is the reason its API looks the way it does, so the relevant question isn't "does it have an ECS" but "does this style of code suit you." It tends to fit well if you're comfortable with Rust structs and functions, want parallelism handled by the engine rather than by hand, and prefer composing behavior from small pieces over building class hierarchies. It's a poorer fit if you want an editor-centric, inspector-driven workflow or you're not working in Rust.

If you want to evaluate it concretely, start by writing one component and one system that queries it — that single loop of "define a struct, write a function, attach the struct to an entity" is the entire model in miniature.