This crate canonicalizes an approach to "blueprint"-based component patterns in Bevy.
This allows users to attach one component, referred to here as the blueprint, and automatically attaches an associated Bundle
to the entity (or spawns the Bundle as a child entity). This crate refers to the associated Bundle
and any child entities created by this process as prefabs associated with the blueprint. (I do not know whether this is entirely appropriate, but the value provided by the crate is similar to prefabs in typical game development environments.)
The supported workflow involves defining a type that serves as a builder for some Bundle
, attaching the appropriate plugin, and spawning entities with (or attaching to existing entities) a Blueprint::new(my_type)
. The plugins provided in bevy_reactive_blueprints
attach systems that reactively update and remove prefab data, so updates to a Blueprint
will remove and re-attach components and children as necessary.
It is worth noting that the intention is not to encourage frequently editing blueprint types in the application. Blueprints are best used for spawning and saving scenes or for development contexts.
In particular, this crate is used well alongside bevy_editor_pls
so that the various "object kinds" in a given application can be spawned and manipulated using the blueprints. An example is provided in the examples
directory. Scene files can also strictly save Blueprint components, minimizing the amount of data stored.
First, define the types that will serve as your blueprints and prefab bundles. The blueprint type should implement Default
and bevy::prelude::Reflect
.
Add BlueprintsPlugin
to your app:
let mut app = App::new();
// ...
app.add_plugins(BlueprintsPlugin);
This configures BlueprintsSet
, a SystemSet
where inner systems are attached, and adds apply_deferred
so that commands are flushed after building the associated prefabs.
Then, individual blueprints can be defined by attaching a BlueprintPlugin
for each pair of types that needs to be managed. BlueprintPlugin
accepts three type parameters:
- The type that will serve as the blueprint (which does not need to be a component but can be). This should implement
Default
andReflect
. - The
Bundle
that should be spawned, which needs to implement theFromBlueprint<MyType>
trait (see below). - Optionally, the
AsChild
type can be used here to instantiate the prefab as a child entity.
let mut app = App::new();
// ...
app.add_plugins(BlueprintsPlugin);
app.add_plugins(BlueprintPlugin::<MyBlueprint, MyPrefabBundle>::default());
// OR
app.add_plugins(BlueprintPlugin::<MyBlueprint, MyPrefabBundle, AsChild>::default());
Blueprints can have various prefabs, leading to composable behavior:
let mut app = App::new();
// ...
// SelfPrefabBundle1 and SelfPrefabBundle2 will be attached to the same entity,
// and ChildPrefabBundle will spawn a child entity with the bundle.
app.add_plugins(BlueprintPlugin::<MyBlueprint, SelfPrefabBundle1>::default());
app.add_plugins(BlueprintPlugin::<MyBlueprint, SelfPrefabBundle2>::default());
app.add_plugins(BlueprintPlugin::<MyBlueprint, ChildPrefabBundle, AsChild>::default());
When doing this, be sure to respect Bevy's typical rules: if SelfPrefabBundle1
and SelfPrefabBundle2
share components, this will cause panics.
In order for this to work, prefab bundles must implement the FromBlueprint
trait. This requires defining the associated type Params: SystemParam
which is used in the from_blueprint
method to provide any system parameters necessary to perform the conversion.
If no system params are necessary, this might look as follows:
impl FromBlueprint<MyType> for MyPrefabBundle {
// this is some bevy::ecs::SystemParam
type Params<'w, 's> = ();
fn from_blueprint(
blueprint: &Rect,
params: &mut StaticSystemParam<Self::Params<'_, '_>>,
) -> Self {
MyPrefabBundle { /* ... */ }
}
}
However, system parameters are frequently necessary to build prefab bundles, especially in order to access assets. This might look like the following:
#[derive(Bundle)]
struct MyPrefabBundle {
pbr: PbrBundle,
}
#[derive(SystemParam)]
struct MyBlueprintParams<'w> {
meshes: ResMut<'w, Assets<Mesh>>,
materials: ResMut<'w, Assets<StandardMaterial>>,
}
impl FromBlueprint<MyType> for MyPrefabBundle {
type Params<'w, 's> = MyBlueprintParams<'w>;
fn from_blueprint<'w, 's>(
blueprint: &MyType,
params: &mut StaticSystemParam<Self::Params<'w, 's>>,
) -> Self {
MyPrefabBundle {
pbr: PbrBundle {
mesh: params.meshes.add(todo!()),
material: params.materials.add(todo!()),
transform: todo!(),
..default()
},
}
}
}
See the tests (and the example in the editor crate) for more information.
- Add docstrings.
- Only require Reflect conditionally.
- Build a crate that defines a helpful window for
bevy_editor_pls
. - Consider reorganizing so that an app extension trait could be used to register blueprint types. This might be more complicated than necessary, but would be nice for ergonomics.
Beware that this calls despawn_recursive
and despawn_descendants
to handle cleanup, so attaching child entities that aren't related to any blueprint is probably going to cause problems.
If you have trouble getting the plugin to work, make sure that (1) your blueprint implements Default
and bevy::prelude::Reflect
and (2) your prefab implements FromBlueprint
.