legion: remove foreach system functions

this is a bit sad, but upstream legion's new lifetimes appear to be incompatible with our foreach approach
This commit is contained in:
Carter Anderson 2020-06-27 12:06:12 -07:00
parent 981687ae41
commit 1f12964026
17 changed files with 215 additions and 537 deletions

View File

@ -1,6 +1,6 @@
use crate::time::Time;
use bevy_property::Properties;
use legion::prelude::{ComMut, Res};
use legion::prelude::{Query, Res, SubWorld, Write};
use std::time::Duration;
#[derive(Clone, Debug, Default, Properties)]
@ -37,6 +37,8 @@ impl Timer {
}
}
pub fn timer_system(time: Res<Time>, mut timer: ComMut<Timer>) {
timer.tick(time.delta_seconds);
pub fn timer_system(time: Res<Time>, world: &mut SubWorld, query: &mut Query<Write<Timer>>) {
for mut timer in query.iter_mut(world) {
timer.tick(time.delta_seconds);
}
}

View File

@ -17,13 +17,11 @@ pub mod world;
#[cfg(feature = "serialize")]
pub mod serialize;
mod system_fn_types;
mod tuple;
mod zip;
pub mod prelude {
pub use crate::{
borrow::{Ref as Com, RefMut as ComMut},
command::CommandBuffer,
entity::Entity,
event::Event,

View File

@ -1,113 +0,0 @@
use crate::{
borrow::{Ref, RefIter, RefIterMut, RefMut},
filter::{ComponentFilter, EntityFilterTuple, Passthrough},
index::{ChunkIndex, SetIndex},
query::{DefaultFilter, View, ViewElement},
storage::{ArchetypeData, Component, ComponentStorage, ComponentTypeId},
};
use std::{
any::TypeId,
slice::{Iter, IterMut},
};
impl<'a, T: Component> DefaultFilter for RefMut<'static, T> {
type Filter = EntityFilterTuple<ComponentFilter<T>, Passthrough, Passthrough>;
fn filter() -> Self::Filter { super::filter::filter_fns::component() }
}
impl<'a, T: Component> View<'a> for RefMut<'static, T> {
type Iter = RefIterMut<'a, T, IterMut<'a, T>>;
#[inline]
fn fetch(
_: &'a ArchetypeData,
chunk: &'a ComponentStorage,
_: ChunkIndex,
_: SetIndex,
) -> Self::Iter {
let (slice_borrow, slice) = unsafe {
chunk
.components(ComponentTypeId::of::<T>())
.unwrap_or_else(|| {
panic!(
"Component of type {:?} not found in chunk when fetching Write view",
std::any::type_name::<T>()
)
})
.data_slice_mut::<T>()
.deconstruct()
};
RefIterMut::new(slice_borrow, slice.iter_mut())
}
#[inline]
fn validate() -> bool { true }
#[inline]
fn reads<D: Component>() -> bool { TypeId::of::<T>() == TypeId::of::<D>() }
#[inline]
fn writes<D: Component>() -> bool { TypeId::of::<T>() == TypeId::of::<D>() }
#[inline]
fn read_types() -> Vec<ComponentTypeId> { vec![ComponentTypeId::of::<T>()] }
#[inline]
fn write_types() -> Vec<ComponentTypeId> { vec![ComponentTypeId::of::<T>()] }
}
impl<'a, T: Component> ViewElement for RefMut<'static, T> {
type Component = T;
}
impl<'a, T: Component> DefaultFilter for Ref<'static, T> {
type Filter = EntityFilterTuple<ComponentFilter<T>, Passthrough, Passthrough>;
fn filter() -> Self::Filter { super::filter::filter_fns::component() }
}
impl<'a, T: Component> View<'a> for Ref<'static, T> {
type Iter = RefIter<'a, T, Iter<'a, T>>;
#[inline]
fn fetch(
_: &'a ArchetypeData,
chunk: &'a ComponentStorage,
_: ChunkIndex,
_: SetIndex,
) -> Self::Iter {
let (slice_borrow, slice) = unsafe {
chunk
.components(ComponentTypeId::of::<T>())
.unwrap_or_else(|| {
panic!(
"Component of type {:?} not found in chunk when fetching Write view",
std::any::type_name::<T>()
)
})
.data_slice::<T>()
.deconstruct()
};
RefIter::new(slice_borrow, slice.iter())
}
#[inline]
fn validate() -> bool { true }
#[inline]
fn reads<D: Component>() -> bool { TypeId::of::<T>() == TypeId::of::<D>() }
#[inline]
fn writes<D: Component>() -> bool { false }
#[inline]
fn read_types() -> Vec<ComponentTypeId> { vec![ComponentTypeId::of::<T>()] }
#[inline]
fn write_types() -> Vec<ComponentTypeId> { Vec::new() }
}
impl<'a, T: Component> ViewElement for Ref<'static, T> {
type Component = T;
}

View File

@ -21,164 +21,6 @@ fn get_idents(fmt_string: fn(usize) -> String, count: usize) -> Vec<Ident> {
.collect::<Vec<Ident>>()
}
#[proc_macro]
pub fn impl_fn_systems(_input: TokenStream) -> TokenStream {
let max_resources = 8;
let max_views = 8;
let resources = get_idents(|i| format!("R{}", i), max_resources);
let resource_vars = get_idents(|i| format!("r{}", i), max_resources);
let views = get_idents(|i| format!("V{}", i), max_views);
let view_vars = get_idents(|i| format!("v{}", i), max_views);
let mut tokens = TokenStream::new();
let command_buffer = vec![Ident::new("CommandBuffer", Span::call_site())];
let command_buffer_var = vec![Ident::new("_command_buffer", Span::call_site())];
for resource_count in 0..=max_resources {
let resource = &resources[0..resource_count];
let resource_var = &resource_vars[0..resource_count];
let resource_tuple = tuple(resource);
let resource_var_tuple = tuple(resource_var);
let resource_access = if resource_count == 0 {
quote! { Access::default() }
} else {
quote! {{
let mut resource_access: Access<ResourceTypeId> = Access::default();
resource_access
.reads
.extend(<#resource_tuple as ResourceSet>::read_types().iter());
resource_access
.writes
.extend(<#resource_tuple as ResourceSet>::write_types().iter());
resource_access
}}
};
for view_count in 0..=max_views {
let view = &views[0..view_count];
let view_var = &view_vars[0..view_count];
let view_tuple = tuple(view);
let component_access = if view_count == 0 {
quote! { Access::default() }
} else {
quote! {{
let mut component_access: Access<ComponentTypeId> = Access::default();
component_access
.reads
.extend(<#view_tuple as View>::read_types().iter());
component_access
.writes
.extend(<#view_tuple as View>::write_types().iter());
component_access
}}
};
let system_query = if view_count == 0 {
quote! { () }
} else if view_count == 1 {
quote! { SystemQuery<
#(#view),*,
#(<#view as DefaultFilter>::Filter),*,
> }
} else {
quote! { SystemQuery<
(#(#view),*),
EntityFilterTuple<
And<(
#(<<#view as DefaultFilter>::Filter as EntityFilter>::ArchetypeFilter),*
)>,
And<(
#(<<#view as DefaultFilter>::Filter as EntityFilter>::ChunksetFilter),*
)>,
And<(
#(<<#view as DefaultFilter>::Filter as EntityFilter>::ChunkFilter),*
)>,
>
> }
};
let query = if view_count == 0 {
quote! {()}
} else {
quote! {<#view_tuple>::query()}
};
for command_buffer_index in 0..2 {
let command_buffer = &command_buffer[0..command_buffer_index];
let command_buffer_var = &command_buffer_var[0..command_buffer_index];
let run_fn = if view_count == 0 {
quote! { self(#(#resource_var,)* #(#command_buffer_var,)*) }
} else {
quote! {
for (#(#view_var),*) in _query.iter_mut(_world) {
self(#(#resource_var.clone(),)* #(#command_buffer_var,)* #(#view_var),*);
}
}
};
tokens.extend(TokenStream::from(quote! {
impl<'a,
Func,
#(#resource: ResourceSet<PreparedResources = #resource> + 'static + Clone,)*
#(#view: for<'b> View<'b> + DefaultFilter + ViewElement,)*
> IntoSystem<(#(#command_buffer)*), (#(#resource,)*), (#(#view,)*), ()> for Func
where
Func: FnMut(#(#resource,)* #(&mut #command_buffer,)* #(#view),*) + Send + Sync + 'static,
#(<#view as View<'a>>::Iter: Iterator<Item = #view>,
<#view as DefaultFilter>::Filter: Sync),*
{
fn system_id(mut self, id: SystemId) -> Box<dyn Schedulable> {
let resource_access: Access<ResourceTypeId> = #resource_access;
let component_access: Access<ComponentTypeId> = #component_access;
let run_fn = FuncSystemFnWrapper(
move |_command_buffer,
_world,
_resources: #resource_tuple,
_query: &mut #system_query
| {
let #resource_var_tuple = _resources;
#run_fn
},
PhantomData,
);
Box::new(FuncSystem {
name: id,
queries: AtomicRefCell::new(#query),
access: SystemAccess {
resources: resource_access,
components: component_access,
tags: Access::default(),
},
archetypes: ArchetypeAccess::Some(BitSet::default()),
_resources: PhantomData::<#resource_tuple>,
command_buffer: FxHashMap::default(),
run_fn: AtomicRefCell::new(run_fn),
})
}
fn system_named(self, name: &'static str) -> Box<dyn Schedulable> {
self.system_id(name.into())
}
fn system(self) -> Box<dyn Schedulable> {
self.system_id(std::any::type_name::<Self>().to_string().into())
}
}
}));
}
}
}
tokens
}
#[proc_macro]
pub fn impl_fn_query_systems(_input: TokenStream) -> TokenStream {
let max_resources = 8;
@ -193,6 +35,8 @@ pub fn impl_fn_query_systems(_input: TokenStream) -> TokenStream {
let command_buffer = vec![Ident::new("CommandBuffer", Span::call_site())];
let command_buffer_var = vec![Ident::new("_command_buffer", Span::call_site())];
let subworld = vec![Ident::new("SubWorld", Span::call_site())];
let subworld_var = vec![Ident::new("_world", Span::call_site())];
for resource_count in 0..=max_resources {
let resource = &resources[0..resource_count];
let resource_var = &resource_vars[0..resource_count];
@ -215,12 +59,14 @@ pub fn impl_fn_query_systems(_input: TokenStream) -> TokenStream {
}}
};
for query_count in 1..=max_queries {
for query_count in 0..=max_queries {
let view = &views[0..query_count];
let query_var = &query_vars[0..query_count];
let view_tuple = tuple(view);
let query_var_tuple = tuple(query_var);
let subworld = &subworld[0..query_count.min(1)];
let subworld_var = &subworld_var[0..query_count.min(1)];
let component_access = if query_count == 0 {
quote! { Access::default() }
@ -241,19 +87,13 @@ pub fn impl_fn_query_systems(_input: TokenStream) -> TokenStream {
let command_buffer = &command_buffer[0..command_buffer_index];
let command_buffer_var = &command_buffer_var[0..command_buffer_index];
let view_tuple_avoid_type_collision = if query_count == 1 {
quote! {(#(#view)*,)}
} else {
quote! {(#(#view,)*)}
};
tokens.extend(TokenStream::from(quote! {
impl<Func,
#(#resource: ResourceSet<PreparedResources = #resource> + 'static + Clone,)*
#(#view: for<'b> View<'b> + DefaultFilter + ViewElement),*
> IntoSystem<(#(#command_buffer)*), (#(#resource,)*), (), #view_tuple_avoid_type_collision> for Func
> IntoSystem<(#(#command_buffer)*), (#(#resource,)*), (#(#view,)*)> for Func
where
Func: FnMut(#(#resource,)*#(&mut #command_buffer,)* &mut SubWorld, #(&mut SystemQuery<#view, <#view as DefaultFilter>::Filter>),*) + Send + Sync + 'static,
Func: FnMut(#(#resource,)*#(&mut #command_buffer,)* #(&mut #subworld,)* #(&mut SystemQuery<#view, <#view as DefaultFilter>::Filter>),*) + Send + Sync + 'static,
#(<#view as DefaultFilter>::Filter: Sync),*
{
fn system_id(mut self, id: SystemId) -> Box<dyn Schedulable> {
@ -268,7 +108,7 @@ pub fn impl_fn_query_systems(_input: TokenStream) -> TokenStream {
| {
let #resource_var_tuple = _resources;
let #query_var_tuple = _queries;
self(#(#resource_var,)*#(#command_buffer_var,)*_world,#(#query_var),*)
self(#(#resource_var,)*#(#command_buffer_var,)*#(#subworld_var,)*#(#query_var),*)
},
PhantomData,
);

View File

@ -9,20 +9,18 @@ use fxhash::FxHashMap;
use legion_core::{
borrow::AtomicRefCell,
command::CommandBuffer,
filter::{And, EntityFilter, EntityFilterTuple},
query::{DefaultFilter, IntoQuery, View, ViewElement},
storage::ComponentTypeId,
};
use legion_fn_system_macro::{impl_fn_query_systems, impl_fn_systems};
use legion_fn_system_macro::impl_fn_query_systems;
use std::marker::PhantomData;
pub trait IntoSystem<CommandBuffer, Resources, Views, Queries> {
pub trait IntoSystem<CommandBuffer, Resources, Queries> {
fn system_id(self, id: SystemId) -> Box<dyn Schedulable>;
fn system_named(self, name: &'static str) -> Box<dyn Schedulable>;
fn system(self) -> Box<dyn Schedulable>;
}
impl_fn_systems!();
impl_fn_query_systems!();
#[allow(type_alias_bounds)]
@ -33,14 +31,8 @@ where
#[cfg(test)]
mod tests {
use crate::{
resource::Resources,
system_fn_types::{Res, ResMut},
IntoSystem, Query, SubWorld,
};
use crate::{resource::Resources, system_fn_types::Res, IntoSystem, Query, SubWorld};
use legion_core::{
borrow::{Ref, RefMut},
command::CommandBuffer,
query::{Read, Write},
world::World,
};
@ -68,10 +60,12 @@ mod tests {
println!("{:?}", x);
}
}
let mut system = query_system.system();
system.run(&mut world, &mut resources);
fn query_system2(
world: &mut SubWorld,
a: Res<A>,
world: &mut SubWorld,
query: &mut Query<(Read<X>, Write<Y>)>,
query2: &mut Query<Read<X>>,
) {
@ -86,98 +80,14 @@ mod tests {
}
}
let mut system = query_system.system();
let mut system2 = query_system2.system();
system.run(&mut world, &mut resources);
system2.run(&mut world, &mut resources);
}
#[test]
fn test_into_system() {
let mut world = World::new();
let mut resources = Resources::default();
resources.insert(A(0));
world.insert((), vec![(X(1), Y(1)), (X(2), Y(2))]);
fn single_read_system(x: Ref<X>) {
println!("{}", x.0);
}
let mut system = single_read_system.system();
system.run(&mut world, &mut resources);
fn read_write_system(x: Ref<X>, y: Ref<Y>, mut z: RefMut<A>) {
z.0 += 1;
println!("{} {} {}", x.0, y.0, z.0);
fn query_system3(a: Res<A>) {
println!("{:?}", *a);
}
({
|x: Res<A>, y: Ref<Y>, mut z: RefMut<A>| {
z.0 += 1;
println!("{} {} {}", x.0, y.0, z.0);
}
})
.system();
let mut system = read_write_system.system();
system.run(&mut world, &mut resources);
fn resource_system(a: Res<A>, x: Ref<X>, y: Ref<Y>) {
println!("{} {} {}", a.0, x.0, y.0);
}
let mut system = resource_system.system();
system.run(&mut world, &mut resources);
fn empty_system_mut() {
println!("hello world");
}
let mut system = empty_system_mut.system();
system.run(&mut world, &mut resources);
fn resource_system_mut(mut a: ResMut<A>, x: Ref<X>, y: Ref<Y>) {
a.0 += 1;
println!("{} {} {}", a.0, x.0, y.0);
}
let mut system = resource_system_mut.system();
system.run(&mut world, &mut resources);
fn command_buffer_system(command_buffer: &mut CommandBuffer, mut a: ResMut<A>) {
a.0 += 1;
command_buffer.insert((), vec![(X(1), Y(1)), (X(2), Y(2))]);
println!("{}", a.0);
}
let mut system = command_buffer_system.system();
system.run(&mut world, &mut resources);
}
#[test]
fn test_resource_system_fn() {
fn my_system(mut a: ResMut<A>, x: Ref<X>, mut y: RefMut<Y>) {
if a.0 == 0 {
assert_eq!(*a, A(0));
assert_eq!(*x, X(2));
assert_eq!(*y, Y(3));
} else if a.0 == 1 {
assert_eq!(*a, A(1));
assert_eq!(*x, X(4));
assert_eq!(*y, Y(5));
y.0 += 1;
assert_eq!(*y, Y(6));
} else {
panic!("unexpected value");
}
a.0 += 1;
}
let mut world = World::new();
let mut resources = Resources::default();
resources.insert(A(0));
resources.insert(B(1));
world.insert((), vec![(X(2), Y(3)), (X(4), Y(5))]);
let mut my_system = my_system.system();
my_system.run(&mut world, &mut resources);
let mut system3 = query_system3.system();
system3.run(&mut world, &mut resources);
}
}

View File

@ -13,8 +13,8 @@ use crate::{
use bevy_asset::{Assets, Handle};
use bevy_property::Properties;
use legion::{
prelude::{ComMut, Res, ResourceSet},
systems::{resource::ResourceTypeId, ResMut},
prelude::{Res, ResourceSet, Write},
systems::{resource::ResourceTypeId, ResMut, SubWorld, Query},
};
use std::{
ops::{Deref, DerefMut, Range},
@ -340,6 +340,8 @@ pub trait Drawable {
fn draw(&mut self, draw: &mut Draw, context: &mut DrawContext) -> Result<(), DrawError>;
}
pub fn clear_draw_system(mut draw: ComMut<Draw>) {
draw.clear_render_commands();
pub fn clear_draw_system(world: &mut SubWorld, query: &mut Query<Write<Draw>>) {
for mut draw in query.iter_mut(world) {
draw.clear_render_commands();
}
}

View File

@ -5,7 +5,10 @@ use crate::{
};
use bevy_asset::Handle;
use bevy_property::Properties;
use legion::{prelude::ComMut, systems::ResMut};
use legion::{
prelude::Write,
systems::{Query, ResMut, SubWorld},
};
#[derive(Properties, Default, Clone)]
pub struct RenderPipeline {
pub pipeline: Handle<PipelineDescriptor>,
@ -104,12 +107,14 @@ impl<'a> Drawable for DrawableRenderPipelines<'a> {
pub fn draw_render_pipelines_system(
mut draw_context: DrawContext,
mut render_resource_bindings: ResMut<RenderResourceBindings>,
mut draw: ComMut<Draw>,
mut render_pipelines: ComMut<RenderPipelines>,
world: &mut SubWorld,
query: &mut Query<(Write<Draw>, Write<RenderPipelines>)>,
) {
let mut drawable = DrawableRenderPipelines {
render_pipelines: &mut render_pipelines,
render_resource_bindings: &mut render_resource_bindings,
};
drawable.draw(&mut draw, &mut draw_context).unwrap();
for (mut draw, mut render_pipelines) in query.iter_mut(world) {
let mut drawable = DrawableRenderPipelines {
render_pipelines: &mut render_pipelines,
render_resource_bindings: &mut render_resource_bindings,
};
drawable.draw(&mut draw, &mut draw_context).unwrap();
}
}

View File

@ -1,6 +1,9 @@
use crate::{texture::Texture, RenderPipelines};
use bevy_asset::{Assets, Handle};
use legion::prelude::{Com, ComMut, Res};
use legion::{
prelude::{Read, Res, Write},
systems::{Query, SubWorld},
};
pub use bevy_derive::ShaderDefs;
@ -55,46 +58,52 @@ impl ShaderDef for Option<Handle<Texture>> {
}
}
pub fn shader_defs_system<T>(shader_defs: Com<T>, mut render_pipelines: ComMut<RenderPipelines>)
pub fn shader_defs_system<T>(world: &mut SubWorld, query: Query<(Read<T>, Write<RenderPipelines>)>)
where
T: ShaderDefs + Send + Sync + 'static,
{
for shader_def in shader_defs.iter_shader_defs() {
for (shader_defs, mut render_pipelines) in query.iter_mut(world) {
for shader_def in shader_defs.iter_shader_defs() {
for render_pipeline in render_pipelines.pipelines.iter_mut() {
render_pipeline
.specialization
.shader_specialization
.shader_defs
.insert(shader_def.to_string());
}
}
}
}
pub fn clear_shader_defs_system(world: &mut SubWorld, query: &mut Query<Write<RenderPipelines>>) {
for mut render_pipelines in query.iter_mut(world) {
for render_pipeline in render_pipelines.pipelines.iter_mut() {
render_pipeline
.specialization
.shader_specialization
.shader_defs
.insert(shader_def.to_string());
.clear();
}
}
}
pub fn clear_shader_defs_system(mut render_pipelines: ComMut<RenderPipelines>) {
for render_pipeline in render_pipelines.pipelines.iter_mut() {
render_pipeline
.specialization
.shader_specialization
.shader_defs
.clear();
}
}
pub fn asset_shader_defs_system<T>(
assets: Res<Assets<T>>,
asset_handle: Com<Handle<T>>,
mut render_pipelines: ComMut<RenderPipelines>,
world: &mut SubWorld,
query: &mut Query<(Read<Handle<T>>, Write<RenderPipelines>)>,
) where
T: ShaderDefs + Send + Sync + 'static,
{
let shader_defs = assets.get(&asset_handle).unwrap();
for shader_def in shader_defs.iter_shader_defs() {
for render_pipeline in render_pipelines.pipelines.iter_mut() {
render_pipeline
.specialization
.shader_specialization
.shader_defs
.insert(shader_def.to_string());
for (asset_handle, mut render_pipelines) in query.iter_mut(world) {
let shader_defs = assets.get(&asset_handle).unwrap();
for shader_def in shader_defs.iter_shader_defs() {
for render_pipeline in render_pipelines.pipelines.iter_mut() {
render_pipeline
.specialization
.shader_specialization
.shader_defs
.insert(shader_def.to_string());
}
}
}
}

View File

@ -6,10 +6,10 @@ use bevy_render::{
texture::Texture,
Color,
};
use bevy_sprite::{ComMut, TextureAtlas};
use bevy_sprite::TextureAtlas;
use bevy_text::{DrawableText, Font, FontAtlasSet, TextStyle};
use bevy_transform::prelude::Transform;
use legion::prelude::{Com, Res, ResMut};
use legion::prelude::*;
pub struct Label {
pub text: String,
@ -37,25 +37,28 @@ impl Label {
fonts: Res<Assets<Font>>,
mut font_atlas_sets: ResMut<Assets<FontAtlasSet>>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
label: Com<Label>,
world: &mut SubWorld,
query: &mut Query<Read<Label>>,
) {
let font_atlases = font_atlas_sets
.get_or_insert_with(Handle::from_id(label.font.id), || {
FontAtlasSet::new(label.font)
});
// TODO: this call results in one or more TextureAtlases, whose render resources are created in the RENDER_GRAPH_SYSTEMS
// stage. That logic runs _before_ the DRAW stage, which means we cant call add_glyphs_to_atlas in the draw stage
// without our render resources being a frame behind. Therefore glyph atlasing either needs its own system or the TextureAtlas
// resource generation needs to happen AFTER the render graph systems. maybe draw systems should execute within the
// render graph so ordering like this can be taken into account? Maybe the RENDER_GRAPH_SYSTEMS stage should be removed entirely
// in favor of node.update()? Regardless, in the immediate short term the current approach is fine.
font_atlases.add_glyphs_to_atlas(
&fonts,
&mut texture_atlases,
&mut textures,
label.style.font_size,
&label.text,
);
for label in query.iter(world) {
let font_atlases = font_atlas_sets
.get_or_insert_with(Handle::from_id(label.font.id), || {
FontAtlasSet::new(label.font)
});
// TODO: this call results in one or more TextureAtlases, whose render resources are created in the RENDER_GRAPH_SYSTEMS
// stage. That logic runs _before_ the DRAW stage, which means we cant call add_glyphs_to_atlas in the draw stage
// without our render resources being a frame behind. Therefore glyph atlasing either needs its own system or the TextureAtlas
// resource generation needs to happen AFTER the render graph systems. maybe draw systems should execute within the
// render graph so ordering like this can be taken into account? Maybe the RENDER_GRAPH_SYSTEMS stage should be removed entirely
// in favor of node.update()? Regardless, in the immediate short term the current approach is fine.
font_atlases.add_glyphs_to_atlas(
&fonts,
&mut texture_atlases,
&mut textures,
label.style.font_size,
&label.text,
);
}
}
pub fn draw_label_system(
@ -65,26 +68,26 @@ impl Label {
texture_atlases: Res<Assets<TextureAtlas>>,
mut render_resource_bindings: ResMut<RenderResourceBindings>,
mut asset_render_resource_bindings: ResMut<AssetRenderResourceBindings>,
mut draw: ComMut<Draw>,
label: Com<Label>,
node: Com<Node>,
transform: Com<Transform>,
world: &mut SubWorld,
query: &mut Query<(Write<Draw>, Read<Label>, Read<Node>, Read<Transform>)>,
) {
// let position = transform.0 - quad.size / 2.0;
let position = transform.value.w_axis().truncate() - (node.size / 2.0).extend(0.0);
for (mut draw, label, node, transform) in query.iter_mut(world) {
// let position = transform.0 - quad.size / 2.0;
let position = transform.value.w_axis().truncate() - (node.size / 2.0).extend(0.0);
let mut drawable_text = DrawableText::new(
fonts.get(&label.font).unwrap(),
font_atlas_sets
.get(&label.font.as_handle::<FontAtlasSet>())
.unwrap(),
&texture_atlases,
&mut render_resource_bindings,
&mut asset_render_resource_bindings,
position,
&label.style,
&label.text,
);
drawable_text.draw(&mut draw, &mut draw_context).unwrap();
let mut drawable_text = DrawableText::new(
fonts.get(&label.font).unwrap(),
font_atlas_sets
.get(&label.font.as_handle::<FontAtlasSet>())
.unwrap(),
&texture_atlases,
&mut render_resource_bindings,
&mut asset_render_resource_bindings,
position,
&label.style,
&label.text,
);
drawable_text.draw(&mut draw, &mut draw_context).unwrap();
}
}
}

View File

@ -10,14 +10,19 @@ fn main() {
fn animate_sprite_system(
texture_atlases: Res<Assets<TextureAtlas>>,
mut timer: ComMut<Timer>,
mut sprite: ComMut<TextureAtlasSprite>,
texture_atlas_handle: Com<Handle<TextureAtlas>>,
world: &mut SubWorld,
query: &mut Query<(
Write<Timer>,
Write<TextureAtlasSprite>,
Read<Handle<TextureAtlas>>,
)>,
) {
if timer.finished {
let texture_atlas = texture_atlases.get(&texture_atlas_handle).unwrap();
sprite.index = ((sprite.index as usize + 1) % texture_atlas.textures.len()) as u32;
timer.reset();
for (mut timer, mut sprite, texture_atlas_handle) in query.iter_mut(world) {
if timer.finished {
let texture_atlas = texture_atlases.get(&texture_atlas_handle).unwrap();
sprite.index = ((sprite.index as usize + 1) % texture_atlas.textures.len()) as u32;
timer.reset();
}
}
}

View File

@ -11,8 +11,14 @@ fn main() {
}
/// rotates the parent, which will result in the child also rotating
fn rotator_system(time: Res<Time>, _rotator: ComMut<Rotator>, mut rotation: ComMut<Rotation>) {
rotation.0 = rotation.0 * Quat::from_rotation_x(3.0 * time.delta_seconds);
fn rotator_system(
time: Res<Time>,
world: &mut SubWorld,
query: &mut Query<(Read<Rotator>, Write<Rotation>)>,
) {
for (_rotator, mut rotation) in query.iter_mut(world) {
rotation.0 = rotation.0 * Quat::from_rotation_x(3.0 * time.delta_seconds);
}
}
/// set up a simple scene with a "parent" cube and a "child" cube

View File

@ -17,12 +17,14 @@ fn main() {
fn move_cubes(
time: Res<Time>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut translation: ComMut<Translation>,
material_handle: Com<Handle<StandardMaterial>>,
world: &mut SubWorld,
query: &mut Query<(Write<Translation>, Read<Handle<StandardMaterial>>)>,
) {
let material = materials.get_mut(&material_handle).unwrap();
translation.0 += math::vec3(1.0, 0.0, 0.0) * time.delta_seconds;
material.albedo += Color::rgb(-time.delta_seconds, -time.delta_seconds, time.delta_seconds);
for (mut translation, material_handle) in query.iter_mut(world) {
let material = materials.get_mut(&material_handle).unwrap();
translation.0 += math::vec3(1.0, 0.0, 0.0) * time.delta_seconds;
material.albedo += Color::rgb(-time.delta_seconds, -time.delta_seconds, time.delta_seconds);
}
}
fn setup(

View File

@ -12,8 +12,14 @@ fn main() {
}
/// rotates the parent, which will result in the child also rotating
fn rotator_system(time: Res<Time>, _rotator: ComMut<Rotator>, mut rotation: ComMut<Rotation>) {
rotation.0 = rotation.0 * Quat::from_rotation_x(3.0 * time.delta_seconds);
fn rotator_system(
time: Res<Time>,
world: &mut SubWorld,
query: &mut Query<(Read<Rotator>, Write<Rotation>)>,
) {
for (_rotator, mut rotation) in query.iter_mut(world) {
rotation.0 = rotation.0 * Quat::from_rotation_x(3.0 * time.delta_seconds);
}
}
fn camera_order_color_system(

View File

@ -80,21 +80,22 @@ fn new_round_system(game_rules: Res<GameRules>, mut game_state: ResMut<GameState
);
}
// This system runs once for each entity with both the "Player" and "Score" component.
// NOTE: Com<Player> is a read-only component reference, whereas ComMut<Score> can modify the component
fn score_system(player: Com<Player>, mut score: ComMut<Score>) {
let scored_a_point = random::<bool>();
if scored_a_point {
score.value += 1;
println!(
"{} scored a point! Their score is: {}",
player.name, score.value
);
} else {
println!(
"{} did not score a point! Their score is: {}",
player.name, score.value
);
// This system updates the score for each entity with the "Player" and "Score" component.
fn score_system(world: &mut SubWorld, query: &mut Query<(Read<Player>, Write<Score>)>) {
for (player, mut score) in query.iter_mut(world) {
let scored_a_point = random::<bool>();
if scored_a_point {
score.value += 1;
println!(
"{} scored a point! Their score is: {}",
player.name, score.value
);
} else {
println!(
"{} did not score a point! Their score is: {}",
player.name, score.value
);
}
}
// this game isn't very fun is it :)
@ -102,26 +103,11 @@ fn score_system(player: Com<Player>, mut score: ComMut<Score>) {
// This system runs on all entities with the "Player" and "Score" components, but it also
// accesses the "GameRules" resource to determine if a player has won.
// NOTE: resources must always come before components in system functions
// NOTE: resources must always come before worlds/queries in system functions
fn score_check_system(
game_rules: Res<GameRules>,
mut game_state: ResMut<GameState>,
player: Com<Player>,
score: Com<Score>,
) {
if score.value == game_rules.winning_score {
game_state.winning_player = Some(player.name.clone());
}
}
// If you need more control over iteration or direct access to SubWorld, you can also use "query systems"
// This is how you would represent the system above with a "query system"
// NOTE: You can add as many queries as you want, but they must come after all resources (Res/ResMut).
#[allow(dead_code)]
fn query_score_check_system(
world: &mut SubWorld,
game_rules: Res<GameRules>,
mut game_state: ResMut<GameState>,
query: &mut Query<(Read<Player>, Read<Score>)>,
) {
for (player, score) in query.iter(world) {
@ -191,7 +177,7 @@ fn startup_system(world: &mut World, resources: &mut Resources) {
// Normal systems cannot safely access the World instance directly because they run in parallel.
// Our World contains all of our components, so accessing it in parallel is not thread safe.
// Command buffers give us the ability to queue up changes to our World without directly accessing it
// NOTE: Command buffers must always come before resources and components in system functions
// NOTE: Command buffers must always come after resources and before queries in system functions
fn new_player_system(
game_rules: Res<GameRules>,
mut game_state: ResMut<GameState>,
@ -252,9 +238,11 @@ fn thread_local_system(world: &mut World, resources: &mut Resources) {
#[allow(dead_code)]
fn closure_system() -> Box<dyn Schedulable> {
let mut counter = 0;
(move |player: Com<Player>, score: Com<Score>| {
println!("processed: {} {}", player.name, score.value);
println!("this ran {} times", counter);
(move |world: &mut SubWorld, query: &mut Query<(Read<Player>, Read<Score>)>| {
for (player, score) in query.iter(world) {
println!("processed: {} {}", player.name, score.value);
}
println!("this system ran {} times", counter);
counter += 1;
})
.system()
@ -270,9 +258,15 @@ struct State {
// NOTE: this doesn't do anything relevant to our game, it is just here for illustrative purposes
#[allow(dead_code)]
fn stateful_system(mut state: ComMut<State>, player: Com<Player>, score: ComMut<Score>) {
println!("processed: {} {}", player.name, score.value);
println!("this ran {} times", state.counter);
fn stateful_system(
mut state: ResMut<State>,
world: &mut SubWorld,
query: &mut Query<(Read<Player>, Read<Score>)>,
) {
for (player, score) in query.iter(world) {
println!("processed: {} {}", player.name, score.value);
}
println!("this system ran {} times", state.counter);
state.counter += 1;
}

View File

@ -50,11 +50,18 @@ fn atlas_render_system(
}
}
fn text_update_system(mut state: ResMut<State>, time: Res<Time>, mut label: ComMut<Label>) {
state.timer.tick(time.delta_seconds);
if state.timer.finished {
label.text = format!("{}", rand::random::<u8>() as char);
state.timer.reset();
fn text_update_system(
mut state: ResMut<State>,
time: Res<Time>,
world: &mut SubWorld,
query: &mut Query<Write<Label>>,
) {
for mut label in query.iter_mut(world) {
state.timer.tick(time.delta_seconds);
if state.timer.finished {
label.text = format!("{}", rand::random::<u8>() as char);
state.timer.reset();
}
}
}
@ -71,10 +78,7 @@ fn setup(
.entity_with(OrthographicCameraComponents::default())
// texture
.entity_with(LabelComponents {
node: Node::new(
Anchors::TOP_LEFT,
Margins::new(0.0, 250.0, 0.0, 60.0),
),
node: Node::new(Anchors::TOP_LEFT, Margins::new(0.0, 250.0, 0.0, 60.0)),
label: Label {
text: "a".to_string(),
font: font_handle,

View File

@ -12,10 +12,16 @@ fn main() {
.run();
}
fn text_update_system(diagnostics: Res<Diagnostics>, mut label: ComMut<Label>) {
if let Some(fps) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS) {
if let Some(average) = fps.average() {
label.text = format!("FPS: {:.2}", average);
fn text_update_system(
diagnostics: Res<Diagnostics>,
world: &mut SubWorld,
query: &mut Query<Write<Label>>,
) {
for mut label in query.iter_mut(world) {
if let Some(fps) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS) {
if let Some(average) = fps.average() {
label.text = format!("FPS: {:.2}", average);
}
}
}
}
@ -28,10 +34,7 @@ fn setup(asset_server: Res<AssetServer>, command_buffer: &mut CommandBuffer) {
.entity_with(OrthographicCameraComponents::default())
// texture
.entity_with(LabelComponents {
node: Node::new(
Anchors::TOP_LEFT,
Margins::new(0.0, 250.0, 0.0, 60.0),
),
node: Node::new(Anchors::TOP_LEFT, Margins::new(0.0, 250.0, 0.0, 60.0)),
label: Label {
text: "FPS:".to_string(),
font: font_handle,

View File

@ -12,12 +12,14 @@ fn main() {
fn placement_system(
time: Res<Time>,
materials: Res<Assets<ColorMaterial>>,
mut node: ComMut<Node>,
material_handle: Com<Handle<ColorMaterial>>,
world: &mut SubWorld,
query: &mut Query<(Write<Node>, Read<Handle<ColorMaterial>>)>,
) {
let material = materials.get(&material_handle).unwrap();
if material.color.r > 0.2 {
node.position += Vec2::new(0.1 * time.delta_seconds, 0.0);
for (mut node, material_handle) in query.iter_mut(world) {
let material = materials.get(&material_handle).unwrap();
if material.color.r > 0.2 {
node.position += Vec2::new(0.1 * time.delta_seconds, 0.0);
}
}
}