Bevy Version: | 0.14 | (current) |
---|
Exclusive Systems
Exclusive systems are systems that Bevy will not run in parallel
with any other system. They can have full unrestricted access
to the whole ECS World
, by taking a &mut World
parameter.
Inside of an exclusive system, you have full control over all data stored in the ECS. You can do whatever you want.
Some example situations where exclusive systems are useful:
- Dump various entities and components to a file, to implement things like saving and loading of game save files, or scene export from an editor
- Directly spawn/despawn entities, or insert/remove resources, immediately with no delay (unlike when using Commands from a regular system)
- Run arbitrary systems and schedules with your own custom control flow logic
- …
See the direct World access page to learn more about how to do such things.
fn do_crazy_things(world: &mut World) {
// we can do anything with any data in the Bevy ECS here!
}
You need to add exclusive systems to the App, just like regular systems. All scheduling APIs (ordering, run conditions, sets) are supported and work the same as with regular systems.
app.add_systems(Update,
do_crazy_things
.run_if(needs_crazy_things)
.after(do_regular_things)
.before(other_things)
);
Exclusive System Parameters
There are a few other things, besides &mut World
, that can be used as
parameters for exclusive systems:
&mut World
: Full direct access to the ECS WorldLocal<T>
: Data local to the system&mut SystemState<P>
: Emulates a regular system, allowing you to easily access data from the World.P
are the system parameters.&mut QueryState<Q, F = ()>
: Allows you to perform queries on the World, similar to aQuery
in regular systems.
SystemState
can be used to emulate a normal system. You can put regular
system parameters inside. This allows you to access the World
as you would
from a normal system, but you can confine it to a specific scope inside your
function body, making it more flexible.
QueryState
is the same thing, but for a single query. It is a simpler
alternative to SystemState
for when you just need to be able to query for
some data.
use bevy::ecs::system::SystemState;
fn spawn_particles_for_enemies(
world: &mut World,
// behaves sort of like a query in a regular system
q_enemies: &mut QueryState<&Transform, With<Enemy>>,
// emulates a regular system with an arbitrary set of parameters
params: &mut SystemState<(
ResMut<MyGameSettings>,
ResMut<MyParticleTracker>,
Query<&mut Transform, With<Player>>,
EventReader<MyDamageEvent>,
// yes, even Commands ;)
Commands,
)>,
// local resource, just like in a regular system
mut has_run_once: Local<bool>,
) {
// note: unlike with a regular Query, we need to provide the world as an argument.
// The world will only be "locked" for the duration of this loop
for transform in q_enemies.iter(world) {
// TODO: do something with the transforms
}
// create a scope where we can access our things like a regular system
{
let (mut settings, mut tracker, mut q_player, mut evr, commands) =
params.get_mut(world);
// TODO: do things with our resources, query, events, commands, ...
}
// because our SystemState includes Commands,
// we must apply them when we are done
params.apply(world);
// we are now free to directly spawn entities
// because the World is no longer used by anything
// (the SystemState and the QueryState are no longer accessing it)
world.spawn_batch((0..10000) // efficiently spawn 10000 particles
.map(|_| SpriteBundle {
// ...
..Default::default()
})
);
// and, of course, we can use our Local
*has_run_once = true;
}
Note: if your SystemState
includes Commands
, you must call .apply()
after you are done! That is when the deferred operations queued via
commands will be applied to the World
.
Performance Considerations
Exclusive systems, by definition, limit parallelism and multi-threading, as nothing else can access the same ECS World while they run. The whole schedule needs to come to a stop, to accomodate the exclusive system. This can easily introduce a performance bottleneck.
Generally speaking, you should avoid using exclusive systems, unless you need to do something that is only possible with them.
On the other hand, if your alternative is to use commands, and you need to process a huge number of entities, exclusive systems are faster.
Commands
is effectively just a way to ask Bevy do to exclusive World
access for you, at a later time. Going through the commands queue is much
slower than just doing the exclusive access yourself.
Some examples for when exclusive systems can be faster:
- You want to spawn/despawn a ton of entities.
- Example: Setup/cleanup for your whole game map.
- You want to do it every frame.
- Example: Managing hordes of enemies.
Some examples for when normal systems with Commands
can be faster: