Relevant official examples: ecs_guide, startup_system, system_param.

Systems are functions you write, which are run by Bevy.

This is where you implement all your game logic.

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

Some of the options are:

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>),
    // this resource might not exist, so wrap it in an Option
    mut c: Option<ResMut<ResourceC>>,
) {
    if let Some(mut c) = c {
        // do something

The maximum number of top-level system parameters, or tuple members, is 16. You can group/nest them into tuples, if you need to work around that limitation.


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

fn main() {
        // ...

        // run it only once at launch

        // run it every frame update

        // ...

The above is enough for simple projects.

As your project grows more complex, you might want to enhance your app builder with some of the powerful tools that Bevy offers for managing when/how your systems run, such as: explicit ordering with labels, system sets, states, run criteria, and stages.