Bevy Plugins
Bevy enables us to break up our game into smaller features using plugins.
fn some_plugin_system() {
println!("Hello from some plugin system!");
}
fn plugin(app: &mut App) {
app.add_systems(Startup, some_plugin_system);
}
fn main() {
App::new().add_plugins(plugin).run();
}
Plugins are useful for bundling up all the setup and runtime behavior of a feature into something we can toggle on and off. Plugins encapsulate the complexity of the feature into something we can "plug-into" our app.
By performing the necessary setup such as adding systems, resources and events to your game, each plugin is responsible for injecting its behavior into your game loop.
Plugins are how third party crates add functionality to Bevy. For example, avian has a plugin that adds physics to your games.
The Plugin trait
Usually we prefer using simple functions. As long as our function takes an &mut App, Bevy will automatically implement the Plugin trait for us.
However, if our plugin has additional life-cycle requirements we can implement the Plugin trait and define the life-cycle methods we are interested in.
struct CameraPlugin;
impl Plugin for CameraPlugin {
fn finish(&self, _app: &mut App) {
info!("Camera plugin finished setting up");
}
fn build(&self, app: &mut App) {
app.add_systems(Startup, initialize_camera);
}
}
Plugin life-cycle
Each Plugin goes through a life-cycle:
- We add our plugin to the
Appwithadd_plugins. - The plugin's
buildmethod is called immediately and the plugin is registered. - Once the app starts, Bevy polls each plugin's
readymethod until every plugin returnstrue. - Bevy then calls
finishon every plugin. - Bevy calls
cleanupon every plugin, and then the app's schedules start running.
build is where we add our systems, resources, events and other plugins.
ready lets a plugin wait for asynchronous setup (such as initializing a renderer) before the app starts.
finish runs once all plugins are ready and is useful when one plugin depends on another plugin's asynchronous setup.
cleanup runs after all plugins are built and finished, but before the app's schedules execute. It is not called again when the app exits.
Plugin ordering and dependencies
Plugins are built in the same order they are added to the App.
Plugins must be defined statically before the app starts running and cannot be modified at runtime.
Ideally a plugin would fully encapsulate its dependencies, but that's not always possible. Plugins that have dependencies do not currently have a good way to ensure those dependencies.
Plugins are unique by default: adding the same plugin to your app more than once will panic. Uniqueness is based on the plugin's name, which defaults to its type name. You can check is_plugin_added before adding a plugin, or override is_unique to return false for plugins that are meant to be added multiple times.
fn physics_plugin(app: &mut App) {
if !app.is_plugin_added::<avian2d::debug_render::PhysicsDebugPlugin>() {
app.add_plugins(DebugPhysicsPlugin);
}
if !app.is_plugin_added::<bevy::input::InputPlugin>() {
app.add_plugins(bevy::input::InputPlugin);
}
}
The is_plugin_added guard is also convenient in tests, where the app under test may already include a plugin that our plugin also adds.
Plugin configuration
If your plugin becomes complicated enough to demand configuration you can add fields to the struct that implements Plugin.
pub struct CameraPlugin {
debug: bool,
}
fn initialize_camera(mut commands: Commands) {
commands.spawn(Camera2d);
}
impl Plugin for CameraPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, initialize_camera);
if self.debug {
// Do something
}
}
}
fn main() {
App::new()
.add_plugins(CameraPlugin { debug: true })
.run();
}
Plugin groups
Having plugins call other plugins is powerful and leads to a hierarchy of plugin dependencies. This can make it hard to configure.
Collections of plugins with a complicated setup can use the PluginGroup trait, which lets us group related plugins together and configure them later. Bevy also provides a plugin_group! macro for declaring a group from a list of plugins, which is how DefaultPlugins and MinimalPlugins are defined.
This can be a great choice for library authors writing plugins that others can add to their game.
pub struct GamePlugins;
impl PluginGroup for GamePlugins {
fn build(self) -> PluginGroupBuilder {
PluginGroupBuilder::start::<Self>()
.add(CameraPlugin)
.add(PhysicsPlugin)
}
}
This will let us (or anyone consuming your plugins) configure exactly how the set of plugins runs in the context of our app:
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(GamePlugins.build().disable::<PhysicsPlugin>())
.run();
}
The builder also lets us replace a plugin with set, or insert one before or after an existing plugin with add_before and add_after, which is how we control ordering when it matters.
The plugin_group! macro
Writing a PluginGroup by hand takes a bit of boilerplate. For most groups we can use Bevy's plugin_group! macro, which generates the PluginGroup implementation for us. Every plugin in the group must implement Default:
use bevy::{app::plugin_group, prelude::*};
mod camera {
use bevy::prelude::*;
// Every plugin in a `plugin_group!` needs to implement `Default`.
#[derive(Default)]
pub struct CameraPlugin;
impl Plugin for CameraPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, spawn_camera);
}
}
fn spawn_camera(mut commands: Commands) {
commands.spawn(Camera2d);
}
}
#[derive(Default)]
struct PhysicsPlugin {
gravity: f32,
}
impl Plugin for PhysicsPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(Gravity(self.gravity));
}
}
#[derive(Resource)]
struct Gravity(f32);
plugin_group! {
/// The set of plugins that make up our game.
pub struct GamePlugins {
// Plugins from another module use three colons.
camera:::CameraPlugin,
// Plugins in this module use a single colon.
:PhysicsPlugin,
}
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
// The macro builds each plugin with `Default`, so we can replace one
// with `set` to configure it.
.add_plugins(GamePlugins.set(PhysicsPlugin { gravity: 9.8 }))
.run();
}
The macro is not in the prelude, so we import it from bevy::app. Plugins defined in the same module are referenced with a single colon (:PhysicsPlugin), while plugins from another module use three colons (camera:::CameraPlugin). Because the macro builds each plugin with Default, we configure one by replacing it with set after building the group.
The default plugins
Bevy includes DefaultPlugins in the bevy::prelude which gives us all of the obvious things our game would need:
| Plugin | Feature | Description |
|---|---|---|
| DiagnosticsPlugin | Adds core diagnostics | |
| FrameCountPlugin | Adds frame counting functionality | |
| InputPlugin | Adds keyboard and mouse input | |
| PanicHandlerPlugin | Adds sensible panic handling | |
| TaskPoolPlugin | Setup of default task pools for multi-threading | |
| TimePlugin | Adds time functionality | |
| TransformPlugin | Handles Transform components | |
| AccessibilityPlugin | bevy_window | Adds non-GUI accessibility functionality |
| AnimationPlugin | bevy_animation | Adds animation support |
| AntiAliasPlugin | bevy_anti_alias | Adds multi-sample anti-aliasing (MSAA) |
| AssetPlugin | bevy_asset | Adds asset server and resources to load assets |
| AudioPlugin | bevy_audio | Adds support for using sound assets |
| CameraPlugin | bevy_camera | Adds 2D and 3D camera components and systems |
| CiTestingPlugin | bevy_ci_testing | Helps instrument continuous integration |
| ClipboardPlugin | bevy_clipboard | Adds clipboard support to a Bevy app |
| CorePipelinePlugin | bevy_core_pipeline | The core rendering pipeline |
| DefaultPickingPlugins | bevy_picking | Adds picking functionality |
| DlssInitPlugin | dlss | Initializes DLSS support if available |
| GilrsPlugin | bevy_gilrs | Adds support for gamepad inputs |
| GizmoPlugin | bevy_gizmos | Provides an immediate mode drawing api for visual debugging |
| GizmoRenderPlugin | bevy_gizmos_render | Sends gizmos to the renderer |
| GltfPlugin | bevy_gltf | Adds support for loading gltf models |
| HotPatchPlugin | hotpatching | Enables hot-patching of assets |
| ImagePlugin | bevy_image | Adds the Image asset and prepares them to render on your GPU |
| InputDispatchPlugin | bevy_input_focus | Dispatches bubbling keyboard and gamepad button events to the focused entity |
| InputFocusPlugin | bevy_input_focus | Sets up the core input focus system |
| LightPlugin | bevy_light | Adds light components and systems |
| LogPlugin | bevy_log | Adds logging to apps |
| MeshPlugin | bevy_mesh | Adds the Mesh asset and prepares them to render on the GPU |
| PbrPlugin | bevy_pbr | Adds physical based rendering with StandardMaterial etc |
| PipelinedRenderingPlugin | bevy_render | Adds pipelined rendering |
| PostProcessPlugin | bevy_post_process | Adds post processing effects |
| RenderDebugOverlayPlugin | bevy_dev_tools + bevy_pbr | Adds a rendering debug overlay to visualize various renderer buffers |
| RenderPlugin | bevy_render | Sets up rendering backend powered by wgpu crate |
| ScenePlugin | bevy_scene | Adds support for spawning Bevy Scenes |
| ScheduleRunnerPlugin | !bevy_window | Configures an App to run its Schedule according to a given RunMode (added when windowing is disabled) |
| SpritePlugin | bevy_sprite | Handling of sprites (images on our entities) |
| SpriteRenderPlugin | bevy_sprite_render | Adds support for sending sprites to the renderer |
| StatesPlugin | bevy_state | Adds state management for Apps |
| TerminalCtrlCHandlerPlugin | std | Handles Ctrl-C signals in terminal applications |
| TextPlugin | bevy_text | Supports loading fonts and rendering text |
| UiPlugin | bevy_ui | Adds support for UI layouts (flex, grid, etc) |
| UiRenderPlugin | bevy_ui_render | Adds support for sending UI nodes to renderer |
| UiWidgetsPlugins | bevy_ui_widgets | Registers the observers for all of the widgets in this crate |
| WebAssetPlugin | bevy_asset | Adds the http and https asset sources to an app |
| WindowPlugin | bevy_window | Provides an interface to create and manage Window components |
| WinitPlugin | bevy_winit | Interface to create operating system windows (to actually display our game) |
| WorldSerializationPlugin | bevy_world_serialization | Loading and saving collections of entities and components to files |
See cargo features for more details.
The minimal plugins
Instead of DefaultPlugins, Bevy also provides MinimalPlugins which only includes the bare essentials to get an app running:
TaskPoolPluginFrameCountPluginTimePluginScheduleRunnerPluginCiTestingPlugin(with featurebevy_ci_testing)
This can be especially useful during tests to setup an app without any rendering concerns.