Tainted\\Coders

Bevy Plugins

Bevy version: 0.18Last updated:

Bevy enables us to break up our game into smaller features using plugins.

A plugin is any function that takes an &mut App and modifies it. These functions can do anything in your normal App definition, even adding other plugins.

fn plugin(app: &mut App) {
  app.add_system(some_plugin_system);
}

fn main() {
  App::new().add_plugins(plugin);
}

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, but if your plugin has additional life-cycle requirements you can implement the Plugin trait and define the life-cycle methods you are interested in.

pub struct CameraPlugin;

impl Plugin for CameraPlugin {
  fn cleanup(&self, _app: &mut App) {
    info!("Time to clean up");
  }

  fn build(&self, app: &mut App) {
    app.add_systems(Startup, initialize_camera);
  }
}

fn initialize_camera(mut commands: Commands) {
  commands.spawn(Camera2d);
}

Plugin life-cycle

Each Plugin will go through a life-cycle of:

  1. We add our plugin to the App with add_plugins
  2. Our plugin has its build method called
  3. Bevy eventually calls ready on our plugin
  4. Bevy internally polls to ensure all plugins succeeded
  5. Finally, Bevy calls cleanup on our plugin.

Plugin ordering and dependencies

Plugins are run in the order they are added to the App. They are also defined during the app's definition phase before the app starts running. They cannot be changed 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.

Bevy will also panic (by default) if you try and add the same plugin to your app more than once. You can work around this by checking if is_plugin_added before adding it.

fn plugin(app: &mut App) {
    if !app.is_plugin_added::<physics::PhysicsPlugin>() {
        app.add_plugins(physics::PhysicsPlugin);
    }

    if !app.is_plugin_added::<bevy::input::InputPlugin>() {
        app.add_plugins(bevy::input::InputPlugin);
    }
}

This is a good practice for testing so that the test for your plugin can run without running the entire app.

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,
}

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 })
}

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 allow us to group related plugins together and then configure them later.

This can be 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::default())
      .add(PhysicsPlugin::default())
      .add(LogicPlugin)
  }
}

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(
      game::GamePlugins
        .build()
        .disable::<physics::PhysicsPlugin>()
    )
    .run();
}

The default plugins

Bevy includes DefaultPlugins in the bevy::prelude which gives us all of the obvious things our game would need:

PluginDescription
DiagnosticsPluginAdds core diagnostics
DlssInitPluginInitializes DLSS support if available
FrameCountPluginAdds frame counting functionality
InputPluginAdds keyboard and mouse input
PanicHandlerPluginAdds sensible panic handling
ScheduleRunnerPluginConfigures an App to run its Schedule according to a given RunMode
TaskPoolPluginSetup of default task pools for multi-threading
TimePluginAdds time functionality
TransformPluginHandles Transform components

Then depending on the features you enable (all enabled by default) you have more plugins that are added:

PluginFeatureDescription
AccessibilityPluginbevy_windowAdds non-GUI accessibility functionality
AnimationPluginbevy_animationAdds animation support
AntiAliasPluginbevy_anti_aliasAdds multi-sample anti-aliasing (MSAA)
AssetPluginbevy_assetAdds asset server and resources to load assets
AudioPluginbevy_audioAdds support for using sound assets
DefaultPickingPluginsbevy_pickingAdds picking functionality
DevToolsPluginbevy_dev_toolsEnables developer tools in an App
CameraPluginbevy_cameraAdds 2D and 3D camera components and systems
CiTestingPluginbevy_ci_testingHelps instrument continuous integration
CorePipelinePluginbevy_core_pipelineThe core rendering pipeline
GltfPluginbevy_gltfAdds support for loading gltf models
GilrsPluginbevy_gilrsAdds support for gamepad inputs
GizmoPluginbevy_gizmosProvides an immediate mode drawing api for visual debugging
GizmoRenderPluginbevy_gizmos_renderSends gizmos to the renderer
HotPatchPluginhotpatchingEnables hot-patching of assets
ImagePluginbevy_renderAdds the Image asset and prepares them to render on your GPU
LightPluginbevy_lightAdds light components and systems
LogPluginbevy_logAdds logging to apps
MeshPluginbevy_meshAdds the Mesh asset and prepares them to render on the GPU
PbrPluginbevy_pbrAdds physical based rendering with StandardMaterial etc
PostProcessingPluginbevy_post_processAdds post processing effects
PipelinedRenderingPluginbevy_renderAdds pipelined rendering
RenderPluginbevy_renderSets up rendering backend powered by wgpu crate
ScenePluginbevy_sceneLoading and saving collections of entities and components to files
SpritePluginbevy_spriteHandling of sprites (images on our entities)
SpriteRenderPluginbevy_sprite_renderAdds support for sending sprites to the renderer
StatesPluginbevy_stateAdds state management for Apps
TerminalCtrlCHandlerPluginstdHandles Ctrl-C signals in terminal applications
TextPluginbevy_textSupports loading fonts and rendering text
UiPluginbevy_uiAdds support for UI layouts (flex, grid, etc)
UiRenderPluginbevy_ui_renderAdds support for sending UI nodes to renderer
WebAssetPluginbevy_assetAdds the http and https asset sources to an app
WindowPluginbevy_windowProvides an interface to create and manage Window components
WinitPluginbevy_winitInterface to create operating system windows (to actually display our game)

The minimal plugins

Instead of DefaultPlugins, Bevy also provides MinimalPlugins which only includes the bare essentials to get an app running:

This can be especially useful during tests to setup an app without any rendering concerns.