Bevy 0.11: Exclusive Systems
This is meant to be an introduction to exclusive systems and using direct world access.
There are two types of systems that you will encounter regularly in Bevy. Normal systems which are what most people are familiar with. They take
Query and are allowed to run in parallel as long as they don't break rust's aliasing rules. And then exclusive systems, which take
&mut World in the first paramter and can only be run "exclusively" i.e. no other systems can run at the same time.
There are a couple big advantages to exclusive systems. One is that all modifications to world happen immediately. The other being when it's hard to specify all your system params up front. i.e. user callbacks. Some disadvantages would be having to run single threaded and that it can be hard to get multiple things out of the world do to mutability rules.
Adding an Exclusive System to your App
use *; // An exclusive system is a system that takes `&mut World` as the first parameter.
&mut World there are 3 other types that are allowed to follow
&mut World in the function signature.
&mut QueryState and
SystemState are used to get queries and system params from the world respectively. The difference from the normal
derive(SystemParam) is that they need to have the
World provided to them to get data out of the world. This step is done for you automatically when a normal system runs.
You can also just construct these types for use with the world.
; ; let mut world = new; let query_state: = new;
The disadvantage here is that QueryState and SystemState often are caching data to make things work or make things faster. There are also types like EventReader that don't work correctly when they aren't cached.
Local is a system param that works on both normal systems and exclusive systems. It's useful for caching local state in a system. In fact
&mut SystemState and
&mut QueryState is mostly just sugar for
Modifying the World Directly
Normal systems aren't allowed to make structural changes to the world directly. Structure changes are things like inserting or removing components or spawning or despawning an entity. These operations require mutable access to the world which is not allowed from normal systems. From those systems these changes are queued through
Commands and then applied to the
World when a
apply_deferred system is run. This makes things like spawning an entity and then acting on that data more complex. But from an exclusive system these changes happen immediately, so it can be easier if you need to do these more complex actions to do them from an exclusive system.
Spawning and Despawning Entities
In an exclusive system we can observe spawns and despawns immediately.
EntityMut and EntityRef
We can get arbitrary data with an
EntityRef or insert or remove arbitrary data on an entity with
use *; let mut world = new; ; // world.spawn returns an `EntityMut` let mut entity_mut = world.spawn; entity_mut.insert; let entity = entity_mut.id; // remove the name component and get ownership let mut entity_mut = world.entity_mut; let name = entity_mut..unwrap; assert_eq!; // get some data let position = entity_mut..unwrap; assert_eq!;
You might want to use a EntityRef instead of EntityMut when you want to get multiple references.
// entity ref has all the same methods that take &self, but none that take &mut let entity_ref_1 = world.entity; let entity_ref_2 = world.entity; assert!;
If you tried the same with
entity_mut you would get a compile error.
// This will not compile, because rust does not allow 2 mutable references to exist to the world. let entity_mut_1 = world.entity_mut(entity1); let entity_mut_2 = world.entity_mut(entity2); assert!(entity_mut_1.contains::<Name>() && entity_mut_2.contains::<Name>()); // The compile error message: // error[E0499]: cannot borrow `world` as mutable more than once at a time // --> src\lib.rs:203:20 // | // 13 | let entity_mut_1 = world.entity_mut(entity1); // | ------------------------- first mutable borrow occurs here // 14 | let entity_mut_2 = world.entity_mut(entity2); // | ^^^^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here // 15 | assert!(entity_mut_1.contains::<Name>() && entity_mut_2.contains::<Name>()); // | ------------------------------- first borrow later used here
There are also failable versions,
get_entity_mut, that return an option.
Check out the docs for more methods on
let mut query_state = world.; for name in query_state.iter let player_name = world . .single; println!; // use &mut on the component to get a mutable reference and _mut version\ // of `single_mut` or `iter_mut`. let mut player_name = world . .single_mut; player_name.0 = "Matilda".to_string; println!; // prints "Matilda"
Taking ownership of data
running a closure
Notes to Self: More realistic components and resource.
2 ways to get entity component data on world.
EntityMut::get. Do we want both? feels hard to teach when to use one vs the other.