This page is an overview, to give you an idea of the big picture of how Bevy works. Click on the various links to be taken to dedicated pages where you can learn more about each concept.
As mentioned in the ECS Intro, Bevy stores all your data for you and allows you easy and flexible access to whatever you need, wherever you need it.
The ECS's data structure is called the
World. That is what
stores and manages all of the data. For advanced scenarios, is possible to have
multiple worlds, and then each one will behave as its own
separate ECS. However, normally, you just work with the main World that Bevy
sets up for your App.
Conceptually, you can think of it by analogy with tables, like in a database or
spreadsheet. Your different data types (Components) are like
the "columns" of a table, and there can be arbitrarily many "rows"
(Entities) containing values / instances of various components.
Entity ID is like the row number. It's an integer index
that lets you find specific component values.
Component types that are empty
structs (contain no data) are called marker
components. They are useful as "tags" to identify
specific entities, or enable certain behaviors. For example, you could use them
to identify the player entity, to mark enemies that are currently chasing the
player, to select entities to be despawned at the end of the level, etc.
Here is an illustration to help you visualize the logical structure. The checkmarks show what component types are present on each entity. Empty cells mean that the component is not present. In this example, we have a player, a camera, and several enemies.
|107||✓ ||✓||✓ |
|108||✓ ||✓||✓ |
|109||✓ ||✓ |
|110||✓ ||✓||✓ |
|111||✓ ||✓||✓ |
Representing things this way gives you flexibility. For example, you could
Health component for your game. You could then have many entities
representing different things in your game, such as the player, NPCs, or
monsters, all of which can have a
Health value (as well as other relevant
The typical and obvious pattern is to use entities to represent "objects in the game/scene", such as the camera, the player, enemies, lights, props, UI elements, and other things. However, you are not limited to that. The ECS is a general-purpose data structure. You can create entities and components to store any data. For example, you could create an entity to store a bunch of settings or configuration parameters, or other abstract things.
Data stored using Entities and Components is accessed using queries.
For example, if you want to implement a game mechanic, write a system,
specify what component types you want to access, and do your thing. You can either
iterate through all entities that match your specification, or access specific
ones (if you know their
If you want to modify the data structure itself (as opposed to just accessing existing data), such as to create or remove entities and components, that requires special consideration. Bevy cannot change the memory layout while other systems might be running. These operations can be buffered/deferred using Commands, to be applied later when it is safe to do so. You can also get direct World access using exclusive systems, if you want to perform such operations immediately (but without multithreading).
Object-Oriented programming teaches you to think of everything as "objects", where each object is an instance of a "class". The class specifies the data and functionality for all objects of that type, in one place. Every object of that class has the same data (with different values) and the same associated functionality.
This is the opposite of the ECS mentality. In ECS, any entity can have any data (any combination of components). The purpose of entities is to identify that data. Your systems are loose pieces of functionality that can operate on any data. They can easily find what they are looking for, and implement the desired behavior.
If you are an object-oriented programmer, you might be tempted to define a big
struct Player containing all the fields / properties of the player.
In Bevy, this is considered bad practice, because doing it that way can make it
more difficult to work with your data and limit performance. Instead, you should
make things granular, when different pieces of data may be accessed independently.
For example, represent the player in your game as an entity, composed
of separate component types (separate
structs) for things like the
health, XP, or whatever is relevant to your game. You can also attach
standard Bevy components like
explained) to it.
Then, each piece of functionality (each system) can just query for the data it needs. Common functionality (like a health/damage system) can be applied to any entity with the matching components, regardless of whether that's the player or something else in the game.
However, if some data always makes sense to be accessed together, then you
should put it in a single
struct. For example, Bevy's
Color. With these types, the
fields are not likely to be useful independently.
The set / combination of components that a given entity has, is called the entity's Archetype. Bevy keeps track of that internally, to organize the data in RAM. Entities of the same Archetype have their data stored together, which allows the CPU to access and cache it efficiently.
If you add/remove component types on existing entities, you are changing the Archetype, which may require Bevy to copy the data to a different location.
If there is only one global instance (singleton) of something, and it is standalone (not associated with other data), create a Resource.
For example, you could create a resource to store your game's graphics settings, or the data for the currently active game mode or session.
This is a simple way of storing data, when you know you don't need the flexibility of Entities/Components.