Mastering plugin loadings in Bevy - Part 1/2

In Bevy, using plugins is the way to easily adds functionnalities to the Game Engine, exports functionnalities upon each projects and share them with others.

There is already many available plugins to adds basic functionnalities that you may need in your project, like: loading voxel files, 3D mouse picking, render tiled maps, and so on...

You can find an almost exhaustive list of all available plugins on the Bevy's official website.

Create your first plugin

To understand how plugins work, the best way is to create your own. There is a perfect example into the source code:

use bevy::{prelude::*, utils::Duration};

// The plugin struct which holds the plugin's configuration
pub struct PrintMessagePlugin {
    wait_duration: Duration,
    message: String,
}

// The plugin implementation, which only needs to declare the `build` function
// The `build` function will be called once we add the plugin in our project
impl Plugin for PrintMessagePlugin {

    fn build(&self, app: &mut App) {
        // Instanciate a PrintMessageState struct defined below
        // to hold the `message` and `wait_duration`
        let state = PrintMessageState {
            message: self.message.clone(),
            timer: Timer::new(self.wait_duration, true),
        };

        // Inserts the `state` as a resource
        // and adds the `print_message_system` function as a system
        app.insert_resource(state).add_system(print_message_system);
    }

}

struct PrintMessageState {
    message: String,
    timer: Timer,
}

// Function which just prints out the `message` every `wait_duration`
fn print_message_system(mut state: ResMut<PrintMessageState>, time: Res<Time>) {
    if state.timer.tick(time.delta()).finished() {
        info!("{}", state.message);
    }
}

Use our plugin

To use our plugin, we now just need to import it into the scope and use the add_plugin method:

fn main() {
    App::new()
        // We add our plugin
        .add_plugin(PrintMessagePlugin {
            wait_duration: Duration::from_secs(1),
            message: "This is an example plugin".to_string(),
        })

        // We need some of the Bevy's native plugins to make this example work
        // We'll see below how to group plugins and import groups
        .add_plugin(bevy::log::LogPlugin)
        .add_plugin(bevy::core::CorePlugin)
        .add_plugin(bevy::input::InputPlugin)
        .add_plugin(bevy::window::WindowPlugin::default())
        .add_plugin(bevy::winit::WinitPlugin)
        .run();
}

This will prints out the message every second:

2022-01-19T13:49:56.451143Z  INFO bevy_plugin_loadings: This is an example plugin
2022-01-19T13:49:57.450121Z  INFO bevy_plugin_loadings: This is an example plugin
2022-01-19T13:49:58.451520Z  INFO bevy_plugin_loadings: This is an example plugin
2022-01-19T13:49:59.451316Z  INFO bevy_plugin_loadings: This is an example plugin
2022-01-19T13:50:00.450232Z  INFO bevy_plugin_loadings: This is an example plugin

Great! We just created our first re-usable plugin 🥳!

Groups of plugins

Groups of plugins can be created to solve situation where two or more plugins need to work with eachothers.

Again you can find a perfect basic example inside the source code:

use bevy::{app::PluginGroupBuilder, prelude::*};

// The first plugin, displaying "hello" in console
pub struct PrintHelloPlugin;

impl Plugin for PrintHelloPlugin {
    fn build(&self, app: &mut App) {
        app.add_system(print_hello_system);
    }
}

fn print_hello_system() {
    info!("hello");
}

// The second plugin, displaying "world" in the console
pub struct PrintWorldPlugin;

impl Plugin for PrintWorldPlugin {
    fn build(&self, app: &mut App) {
        app.add_system(print_world_system);
    }
}

fn print_world_system() {
    info!("world");
}

/// Then a group joining the two previous plugins
pub struct HelloWorldPlugins;

impl PluginGroup for HelloWorldPlugins {
    fn build(&mut self, group: &mut PluginGroupBuilder) {
        group
            .add(PrintHelloPlugin)
            .add(PrintWorldPlugin);
    }
}

Use a group of plugins

To use a group of plugins, we only need to use the add_plugins function on the App initialization.

In the following example, we are using the native DefaultPlugins which add the native plugins we needed above (and many more plugins):

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

This will display this in the console:

2022-01-19T14:40:13.288344Z  INFO bevy_plugin_loadings: world
2022-01-19T14:40:13.288344Z  INFO bevy_plugin_loadings: hello
2022-01-19T14:40:13.309769Z  INFO bevy_plugin_loadings: world
2022-01-19T14:40:13.309790Z  INFO bevy_plugin_loadings: hello
2022-01-19T14:40:13.316525Z  INFO bevy_plugin_loadings: hello
2022-01-19T14:40:13.316518Z  INFO bevy_plugin_loadings: world

DefaultPlugins and MinimalPlugins

In the above example, we used the DefaultPlugins group, which adds the most used Bevy's native plugins.

Actually this group adds all these plugins:

  • LogPlugin
  • CorePlugin
  • TransformPlugin
  • DiagnosticsPlugin
  • InputPlugin
  • WindowPlugin
  • AssetPlugin
  • ScenePlugin

It conditionnally adds these plugins too, if the corresponding feature is activated in the project (they are activated by default):

  • WinitPlugin
  • RenderPlugin
  • CorePipelinePlugin
  • SpritePlugin
  • TextPlugin
  • UiPlugin
  • PbrPlugin
  • GltfPlugin
  • AudioPlugin
  • GilrsPlugin

To disable some of these default features and so their corresponding plugins in your project to reduce the final bundle size, you need to modify the cargo.toml file:

[dependencies]
# Only loads the winit and bevy features
bevy = { version = "0.6.0", default-features = false, features = ["bevy_winit", "bevy_sprite"] }

The DefaultPlugins group is probably the most used group of plugins, which you'll use in all your projects, but you need to know that there is a minimalist one, that you can find in the same source code file: the MinimalPlugins group, which only loads two plugins:

  • CorePlugin
  • ScheduleRunnerPlugin

In the next article, we'll review how to disable/enable a plugin of a group and how to add your plugin to an existing group, stay tuned!

Comments

Be the first to post a comment!

Add a comment

Preview