Plugins

Relevant official examples: plugin, plugin_group.


As your project grows, it can be useful to make it more modular. You can split it into "plugins".

Plugins are simply collections of things to be added to the App Builder. Think of this as a way to add things to the app from multiple places, like different Rust files/modules or crates.

The simplest way to create a plugin is by just writing a Rust function that takes &mut App:

fn my_plugin(app: &mut App) {
    app.init_resource::<MyCustomResource>();
    app.add_systems(Update, (
        do_some_things,
        do_other_things,
    ));
}

An alternative way is by creating a struct and implementing the Plugin trait:

struct MyPlugin;

impl Plugin for MyPlugin {
    fn build(&self, app: &mut App) {
        app.init_resource::<MyOtherResource>();
        app.add_event::<MyEvent>();
        app.add_systems(Startup, plugin_init);
        app.add_systems(Update, my_system);
    }
}

The benefit of using a struct is that you could extend it with configuration parameters or generics if you want to make your plugin configurable.

Either way, you get &mut access to the App, so you can add whatever you want to it, just like you can do from your fn main().

You can now add your plugins to your App from elsewhere (most commonly fn main()). Bevy will just call your plugin implementation above. In effect, everything the plugin adds will be flattened into your App alongside everything that is already there.

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins((
            my_plugin, // the `fn`-based plugin
            MyPlugin,  // the `struct`-based plugin
        ))
        .run();
}

For internal organization in your own project, the main value of plugins comes from not having to declare all your Rust types and functions as pub, just so they can be accessible from fn main to be added to the app builder. Plugins let you add things to your app from multiple different places, like separate Rust files / modules.

You can decide how plugins fit into the architecture of your game.

Some suggestions:

  • Create plugins for different states.
  • Create plugins for various sub-systems, like physics or input handling.

Plugin Groups

Plugin groups register multiple plugins at once. Bevy's DefaultPlugins and MinimalPlugins are examples of this.

To create your own plugin group, implement the PluginGroup trait:

use bevy::app::PluginGroupBuilder;

struct MyPluginGroup;

impl PluginGroup for MyPluginGroup {
    fn build(self) -> PluginGroupBuilder {
        PluginGroupBuilder::start::<Self>()
            .add(FooPlugin)
            .add(BarPlugin)
    }
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(MyPluginGroup)
        .run();
}

When adding a plugin group to the app, you can disable some plugins while keeping the rest.

For example, if you want to manually set up logging (with your own tracing subscriber), you can disable Bevy's LogPlugin:

App::new()
    .add_plugins(
        DefaultPlugins.build()
            .disable::<bevy::log::LogPlugin>()
    )
    .run();

Note that this simply disables the functionality, but it cannot actually remove the code to avoid binary bloat. The disabled plugins still have to be compiled into your program.

If you want to slim down your build, you should look at disabling Bevy's default cargo features, or depending on the various Bevy sub-crates individually.

Plugin Configuration

Plugins are also a convenient place to store settings/configuration that are used during initialization/startup. For settings that can be changed at runtime, it is recommended that you put them in resources instead.

struct MyGameplayPlugin {
    /// Should we enable dev hacks?
    enable_dev_hacks: bool,
}

impl Plugin for MyGameplayPlugin {
    fn build(&self, app: &mut App) {
        // add our gameplay systems
        app.add_systems(Update, (
            health_system,
            movement_system,
        ));
        // ...

        // if "dev mode" is enabled, add some hacks
        if self.enable_dev_hacks {
            app.add_systems(Update, (
                player_invincibility,
                free_camera,
            ));
        }
    }
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(MyGameplayPlugin {
            // change to true for dev testing builds
            enable_dev_hacks: false,
        })
        .run();
}

Plugins that are added using Plugin Groups can also be configured. Many of Bevy's DefaultPlugins work this way.

use bevy::window::WindowResolution;

App::new()
    .add_plugins(DefaultPlugins.set(
        // here we configure the main window
        WindowPlugin {
            primary_window: Some(Window {
                resolution: WindowResolution::new(800.0, 600.0),
                // ...
                ..Default::default()
            }),
            ..Default::default()
        }
    ))
    .run();

Publishing Crates

Plugins give you a nice way to publish Bevy-based libraries for other people to easily include into their projects.

Bevy offers some official guidance for good practices when you develop plugins you want to publish for other people to use. You can read it here.

Don't forget to submit an entry to Bevy Assets on the official website, so that people can find your plugin more easily. You can do this by making a PR in the Github repo.

If you are interested in supporting bleeding-edge Bevy (main), see here for advice.