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!