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

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.


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.