You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Thank you so much for this book, it has been very enlightening. I understand that you are busy and are yet to finish it, but could I ask in advance how queries are constructed? I have always been baffled at how Bevy knows which queries it needs to construct, and where / how it makes them.
Systems being called with resources makes sense. Resources are added to the Schedule, and then the magic happens in the System trait and its implementations:
When the Schedule calls run, it gives it the resources map, and then the implementation for each number of resources gets that resource from the map using T1::retrieve(resources). Resources are keyed by type ID. Basically, we have converted a function with any number of arguments, into a FunctionSystem, which is always run with one argument (the resources map), where it then retrieves any number of parameters from that map and passes them to the function.
So before a system that uses resources is run, we must add the resources to the schedule. This is the same as how resources work in Bevy.
However, Queries, do not work like this. We do not have to manually add the queries to the scheduler before the system is ran. Bevy somehow automatically constructs the queries and then passes them to the systems. I attempted this by copying the way resources have been implemented, but instead of adding a resource, we can register a query, where a default version is added to the map. I then expect that Bevy automatically registers the queries when the function is converted into a system. Is this correct?
In reality, it's unlikely creates a Default Query. Queries are iterators, so instead of Default, they are probably FromWorld, so it constructs them from the table of components that will be iterated over. However, I'm more interested in where Bevy constructs them and how it knows what types to construct, so making a default version that yields a default value is fine.
Here is my attempt at adding Queries, without automatic registration. This has many issues, and you need to manually call register_query, but otherwise, am I starting in the right direction? Thanks.
externcrate std;use std::collections::HashMap;use std::any::{Any,TypeId};use std::marker::PhantomData;//// System Params//traitSystemParam{typeItem<'new>;fnretrieve<'r>(resources:&'r HashMap<TypeId,Box<dynAny>>,queries:&'r HashMap<TypeId,Box<dynAny>>) -> Self::Item<'r>;}structRes<'a,T:'static>{value:&'a T,}impl<'res,T:'static>SystemParamforRes<'res,T>{typeItem<'new> = Res<'new,T>;fnretrieve<'r>(resources:&'r HashMap<TypeId,Box<dynAny>>,queries:&'r HashMap<TypeId,Box<dynAny>>) -> Self::Item<'r>{let value = resources
.get(&TypeId::of::<T>()).unwrap().downcast_ref().unwrap();Res{ value }}}structQuery<'a,T:'static>{value:&'a T,}impl<'res,T:Default + 'static>SystemParamforQuery<'res,T>{typeItem<'new> = Query<'new,T>;fnretrieve<'r>(resources:&'r HashMap<TypeId,Box<dynAny>>,queries:&'r HashMap<TypeId,Box<dynAny>>) -> Self::Item<'r>{let value = queries
.get(&TypeId::of::<T>()).unwrap().downcast_ref().unwrap();Query{ value }}}//// System Trait//structFunctionSystem<Input,F>{f:F,marker:PhantomData<fn() -> Input>,}//define traittraitSystem{fnrun(&mutself,resources:&mutHashMap<TypeId,Box<dynAny>>,queries:&mutHashMap<TypeId,Box<dynAny>>);}//implement trait for none, one and two parametersimpl<F>SystemforFunctionSystem<(),F>wherefor<'a,'b>&'a mutF:FnMut() + FnMut(),{fnrun(&mutself,resources:&mutHashMap<TypeId,Box<dynAny>>,queries:&mutHashMap<TypeId,Box<dynAny>>){fncall_inner(mutf:implFnMut()){f()}call_inner(&mutself.f)}}impl<F,T1:SystemParam>SystemforFunctionSystem<(T1,),F>wherefor<'a,'b>&'a mutF:FnMut(T1) + FnMut(<T1asSystemParam>::Item<'b>),{fnrun(&mutself,resources:&mutHashMap<TypeId,Box<dynAny>>,queries:&mutHashMap<TypeId,Box<dynAny>>){fncall_inner<T1>(mutf:implFnMut(T1),T1:T1){f(T1)}letT1 = T1::retrieve(resources, queries);call_inner(&mutself.f,T1)}}impl<F,T1:SystemParam,T2:SystemParam>SystemforFunctionSystem<(T1,T2),F>wherefor<'a,'b>&'a mutF:FnMut(T1,T2) + FnMut(<T1asSystemParam>::Item<'b>, <T2asSystemParam>::Item<'b>),{fnrun(&mutself,resources:&mutHashMap<TypeId,Box<dynAny>>,queries:&mutHashMap<TypeId,Box<dynAny>>){fncall_inner<T1,T2>(mutf:implFnMut(T1,T2),T1:T1,T2:T2){f(T1,T2)}letT1 = T1::retrieve(resources, queries);letT2 = T2::retrieve(resources, queries);call_inner(&mutself.f,T1,T2)}}//// Convert Functions to Systems////define traittraitIntoSystem<Input>{typeSystem:System;fninto_system(self) -> Self::System;}//implement trait for none, one and two parametersimpl<F>IntoSystem<()>forFwherefor<'a,'b>&'a mutF:FnMut() + FnMut(),{typeSystem = FunctionSystem<(),Self>;fninto_system(self) -> Self::System{FunctionSystem{f:self,marker:Default::default(),}}}impl<F,T1:SystemParam>IntoSystem<(T1,)>forFwherefor<'a,'b>&'a mutF:FnMut(T1) + FnMut(<T1asSystemParam>::Item<'b>),{typeSystem = FunctionSystem<(T1,),Self>;fninto_system(self) -> Self::System{FunctionSystem{f:self,marker:Default::default(),}}}impl<F,T1:SystemParam,T2:SystemParam>IntoSystem<(T1,T2)>forFwherefor<'a,'b>&'a mutF:FnMut(T1,T2) + FnMut(<T1asSystemParam>::Item<'b>, <T2asSystemParam>::Item<'b>),{typeSystem = FunctionSystem<(T1,T2),Self>;fninto_system(self) -> Self::System{FunctionSystem{f:self,marker:Default::default(),}}}//// Scheduler//typeStoredSystem = Box<dynSystem>;structScheduler{systems:Vec<StoredSystem>,resources:HashMap<TypeId,Box<dynAny>>,queries:HashMap<TypeId,Box<dynAny>>,}implScheduler{pubfnrun(&mutself){for system inself.systems.iter_mut(){
system.run(&mutself.resources,&mutself.queries);}}pubfnadd_system<I,S:System + 'static>(&mutself,system:implIntoSystem<I,System = S>){//perhaps `register_query` should happen here? `into_system` is statically dispatched depending on the//number and type of parameters in the function. Each implementation has access to the types we need to register,//so maybe it should be done in there?self.systems.push(Box::new(system.into_system()));}pubfnadd_resource<R:'static>(&mutself,res:R){self.resources.insert(TypeId::of::<R>(),Box::new(res));}pubfnregister_query<Q:Default + 'static>(&mutself){self.queries.insert(TypeId::of::<Q>(),Box::new(Q::default()));}}//// Example Usage//fnsystem_using_one_query(q:Query<f32>){println!("called bar with query: {}", q.value);}fnmain(){letmut scheduler = Scheduler{systems:Vec::new(),resources:HashMap::default(),queries:HashMap::default(),};
scheduler.add_system(system_using_one_query);
scheduler.register_query::<f32>();//This needs to be called automatically.
scheduler.run();}
The text was updated successfully, but these errors were encountered:
Thank you so much for this book, it has been very enlightening. I understand that you are busy and are yet to finish it, but could I ask in advance how queries are constructed? I have always been baffled at how Bevy knows which queries it needs to construct, and where / how it makes them.
Systems being called with resources makes sense. Resources are added to the Schedule, and then the magic happens in the System trait and its implementations:
When the Schedule calls run, it gives it the
resources
map, and then the implementation for each number of resources gets that resource from the map using T1::retrieve(resources). Resources are keyed by type ID. Basically, we have converted a function with any number of arguments, into aFunctionSystem
, which is alwaysrun
with one argument (the resources map), where it then retrieves any number of parameters from that map and passes them to the function.So before a system that uses resources is run, we must add the resources to the schedule. This is the same as how resources work in Bevy.
However, Queries, do not work like this. We do not have to manually add the queries to the scheduler before the system is ran. Bevy somehow automatically constructs the queries and then passes them to the systems. I attempted this by copying the way resources have been implemented, but instead of
adding
a resource, we canregister
a query, where a default version is added to the map. I then expect that Bevy automatically registers the queries when the function is converted into a system. Is this correct?In reality, it's unlikely creates a
Default
Query. Queries are iterators, so instead ofDefault
, they are probablyFromWorld
, so it constructs them from the table of components that will be iterated over. However, I'm more interested in where Bevy constructs them and how it knows what types to construct, so making a default version that yields a default value is fine.Here is my attempt at adding Queries, without automatic registration. This has many issues, and you need to manually call
register_query
, but otherwise, am I starting in the right direction? Thanks.The text was updated successfully, but these errors were encountered: