Bevy Version: | 0.13 | (outdated!) |
---|
As this page is outdated, please refer to Bevy's official migration guides while reading, to cover the differences: 0.13 to 0.14.
I apologize for the inconvenience. I will update the page as soon as I find the time.
System Sets
System Sets allow you to easily apply common properties to multiple systems, such as ordering and run conditions.
Anything you add to the set will automatically be applied to all systems belonging to the set.
A system can belong to multiple different sets, and will inherit all the properties from all of them. You can also add additional properties to individual systems.
All of this combined gives you a lot of flexibility and control over how your systems run.
Anonymous Sets
The simplest kind of system set is when you just add a tuple of
multiple systems using .add_systems
.
app.add_systems(
Update,
(
system_a,
system_b,
system_c
)
.run_if(common_run_condition)
.after(some_system)
);
This syntax is useful when you just want to apply some common configuration to multiple systems.
Named Sets
This is the more formal and powerful way to use system sets.
You can create a Rust type (struct
or enum
) to serve as a label/identifier,
so you can refer to the set from different places.
For a single set, create an empty struct
. If you need to create multiple
related sets, create an enum
. Every variant of the enum
is a separate system
set.
You need to derive SystemSet
+ an assortment of required standard Rust traits:
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
struct MyAudioSet;
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
struct MyInputSet;
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
enum MyInputKindSet {
Touch,
Mouse,
Gamepad,
}
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
enum MyGameplaySet {
Player,
Enemies,
}
Now, you can apply the set to your systems using .in_set()
:
app.add_systems(Update, (
(
play_music
.run_if(music_enabled),
play_ui_sounds,
).in_set(MyAudioSet),
(
player_movement,
player_animation
.after(player_movement),
player_level_up,
player_footsteps
.in_set(MyAudioSet),
).in_set(MyGameplaySet::Player),
(
enemy_movement,
enemy_ai
.after(MyGameplaySet::Player),
enemy_footsteps
.in_set(MyAudioSet),
).in_set(MyGameplaySet::Enemies),
(
(
mouse_cursor_tracking,
mouse_clicks,
).in_set(MyInputKindSet::Mouse),
(
gamepad_cursor_tracking,
gamepad_buttons,
).in_set(MyInputKindSet::Gamepad),
(
touch_gestures,
).in_set(MyInputKindSet::Touch),
).in_set(MyInputSet),
));
You can add run conditions and ordering
dependencies on your set using .configure_sets
:
app.configure_sets(Update, (
MyAudioSet
.run_if(audio_enabled),
MyGameplaySet::Player
.after(MyInputSet)
.run_if(player_is_alive),
MyGameplaySet::Enemies
.run_if(enemies_present),
MyInputKindSet::Touch
.run_if(touchscreen_enabled),
MyInputKindSet::Mouse
.run_if(mouse_enabled),
MyInputKindSet::Gamepad
.run_if(gamepad_connected),
));
The main use case of named system sets is for logical organization, so that you can manage your systems and refer to the whole group.
Some examples:
- A set for all your audio-related systems, so you can disable them all if sound is muted.
- A set for all your touchscreen input systems, so you can disable them all if there is no touchscreen.
- A set for all your input handling systems, so you can order them to run before gameplay systems.
- A set for all your gameplay systems, so that they only run during the in-game state.
With Plugins
Named sets are also very useful together with plugins. When you are writing
a plugin, you can expose (make pub
) some system set types, to allow users of your
plugin to control how things in your plugin run, or how their things run in relation to
your plugin. This way, you don't have to expose any of your individual systems.
Some examples:
- You are making a physics plugin. Make a set for your whole plugin, so your users can easily order their systems to run before/after physics. They can also easily control whether your physics runs at all, by adding an extra run condition to your set.
- You are using plugins for internal organization in your project. You have an UI plugin. Create a system set for the systems that need to update UI state from gameplay state, so that you can easily add ordering dependencies between UI and gameplay. Other plugins / places in your code now don't need to know about the internals of your UI plugin.
Common Pitfalls
WARNING! System set configuration is stored per-schedule! Notice how
we had to specify .configure_sets(Update, ...)
. It can be very easy to configure your
sets once and then just assume you can use them anywhere, but that is not true.
If you try to use them in a schedule other than the one where you configured them, your code will compile and run (Bevy silently initializes the sets in each schedule), but will not work correctly, as they will not have any of your configurations.
Some common scenarios where this can occur:
- You configure your set in
Update
and try to also use it inFixedUpdate
, or vice versa. - You try to use your sets in the
OnEnter
/OnExit
schedules of various app states. - You add a system to
PostUpdate
orPreUpdate
.