-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Description
What problem does this solve or what need does it fill?
Given access to a &mut World, I need to run queries, modify / create components and access resources. I can only do this in the context of a system. I'd like something simpler.
I use these for world-to-world messaging, sync with outside stuff (Godot), unit tests and actions for my LockStep implimentation.
What solution would you like?
I'd like to be able to do this:
let ( mut commands,
mut client_list,
mut actor_inputs,
mut biped,
mut ships,
) : (Commands,
ResMut<ClientList>,
Query<&mut ActorInput>,
Query<&mut Biped>,
Query<(Entity, &SpaceShip)>) = world.system_data();
another example would be
for (ship, pos) in world.system_data::<Query<(&SpaceShip, &Pos)>>().into_iter() {
built_str +=
format!("{:?} : ({}, {} : {} degrees)\n", ship, pos.x, pos.y, pos.facing).as_str();
}
I initially thought this is what world.query did. It allowed you to unpack any query that you could have made the argument list of a system. I.e anything with signature SystemParam. This is what Specs does with specs::World::system_data.
It could take a mutable reference to the world and lock the entire world until you return the SystemParam, i.e query(&'a mut self) -> SystemParam<'a... >
What alternative(s) have you considered?
I tried using world.query() but it returns just one query and using it is awkward. You need to keep passing world in. I intitially thought I got a useful bevy_ecs::Query
but instead got some QueryState
, which is something I don't know how to turn into a Query
.
I considered using World::resource_scope but it supports just one resource at a time, and not Queries or components at all.
I tried creating some traits for worlds and queries that made them easier to use, but I can't make a method in world that returns an easy to use Iter without storing a reference to world in the iter and ... it started getting too hard.
An iterator in world would allow this (with a custom iterator)
for (ship, pos) in world.query_iter::<(&SpaceShip, &Pos)>(){
// ...
}
I tried making short-lived systems to wrap my code which needed queries, but that requires gymnastics to pass in &self since &self is not 'static. I used rc::Rc and rc::Weak to get around that:
impl ExecutesAction for AdjustFloor {
type UndoData = AdjustFloorUndo;
fn apply(&self, world: &mut World) -> Result<AdjustFloorUndo, ActionResult> {
let undo = AdjustFloorUndo {
area: self.area,
flags: self.flags,
prev_floor: None, // world.get_resource::<Floor>().get_subsection(self.area), // Will need to save previous walls here...
};
let self_ptr = Rc::new(*self);
if self.flag_add() {
(|this:In<Weak<AdjustFloor>>, mut q: (Commands, ResMut<Floor>, ResMut<ModuleMap>)|
{
let this = this.0.upgrade().unwrap();
AdjustFloor::do_add_floors_and_walls(&this, q)
}).system().run(Rc::downgrade(&self_ptr), world);
} else {
(|this:In<Weak<AdjustFloor>>, mut q: (Commands, ResMut<Floor>, ResMut<ModuleMap>)|
{
let this = this.0.upgrade().unwrap();
AdjustFloor::do_remove_floors_and_walls(&this, q)
}).system().run(Rc::downgrade(&self_ptr), world);
}
Ok(undo)
}
In Specs I used to just call do_add_floors_and_walls
with a reference to a specs::World
and then do this:
fn do_add_floors_and_walls(&self, world: &mut World) {
let (mut floor, events, entities, mut modules, sync, mut map) = world.system_data::<(
Write<Floor>,
Read<WorldEvents>,
Entities,
WriteStorage<ProductionModule>,
ReadStorage<SyncStatus>,
Write<ModuleMap>,
)>();
Additional context
My Game (recent blog) has multiple Worlds, one per spaceship. There is one more world that ties them together, the "Space World"
I'm using multiple words because I want each space-ship to be its own deterministic sim and keeping them separate makes this easier.