Relevant official examples: ecs_guide, startup_system, system_param.

Systems are pieces of functionality, which are run by Bevy. They are typically implemented using regular Rust functions.

This is how you implement all your game logic. Each system specifies what data it needs to access to do its thing, and Bevy will run them in parallel when possible.

These functions can only take special parameter types, to specify what data you need access to. If you use unsupported parameter types in your function, you will get confusing compiler errors!

Some of the possibilities are:

See here for a full list!

fn debug_start(
    // access resource
    start: Res<StartingLevel>
) {
    eprintln!("Starting on level {:?}", *start);

System parameters can be grouped into tuples (which can be nested). This is useful for organization.

fn complex_system(
    (a, mut b): (Res<ResourceA>, ResMut<ResourceB>),
    (q0, q1, q2): (Query<(/* … */)>, Query<(/* … */)>, Query<(/* … */)>),
) {
    // …

Your function can have a maximum of 16 total parameters. If you need more, group them into tuples to work around the limit. Tuples can contain up to 16 members, but can be nested indefinitely.

There is also a different kind of systems: exclusive systems. They have full direct access to the ECS World, so you can access any data you want and do anything, but cannot run in parallel. For most use cases, you should use regular parallel systems.

fn reload_game(world: &mut World) {
    // ... access whatever we want from the World


To run your systems, you need to add them to Bevy via the app builder:

fn main() {
        // run these only once at launch
        .add_systems(Startup, (setup_camera, debug_start))
        // run these every frame update
        .add_systems(Update, (move_player, enemies_ai))
        // ...

Be careful: writing a new system fn and forgetting to add it to your app is a common mistake! If you run your project and your new code doesn't seem to be running, make sure you added the system!

The above is enough for simple projects.

Systems are contained in schedules. Update is the schedule where you typically add any systems you want to run every frame. Startup is where you typically add systems that should run only once on app startup. There are also other possibilities.

As your project grows more complex, you might want to make use of some of the powerful tools that Bevy offers for managing when/how your systems run, such as: explicit ordering, run conditions, system sets, states.