From 1720014d2edb3ccc5fbfb7a828a2bbc5b5cd0451 Mon Sep 17 00:00:00 2001 From: yuma14 Date: Mon, 13 May 2024 22:54:32 +0900 Subject: [PATCH 1/2] feat: add PairComponentsRefIter and PairComponentsRefIterMut --- src/bin/usage.rs | 58 ++++++++++++++++- src/ecs.rs | 4 +- src/ecs/iter.rs | 160 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+), 4 deletions(-) diff --git a/src/bin/usage.rs b/src/bin/usage.rs index d364493..c771486 100644 --- a/src/bin/usage.rs +++ b/src/bin/usage.rs @@ -1,4 +1,7 @@ -use xanadu::ecs::{SingleComponentExclusiveIter, SingleComponentExclusiveIterMut, World}; +use xanadu::ecs::{ + PairComponentsRefIter, PairComponentsRefIterMut, SingleComponentExclusiveIter, + SingleComponentExclusiveIterMut, World, +}; #[derive(Debug)] pub struct Position { @@ -7,8 +10,18 @@ pub struct Position { pub z: f64, } +#[derive(Debug)] +pub struct Velocity { + pub x: f64, + pub y: f64, + pub z: f64, +} + fn main() { - let mut world = World::builder().register_component::().build(); + let mut world = World::builder() + .register_component::() + .register_component::() + .build(); for i in 0..5 { let entity = world.new_entity(); world.attach_component( @@ -20,6 +33,25 @@ fn main() { }, ); } + for i in 0..3 { + let entity = world.new_entity(); + world.attach_component( + entity, + Position { + x: i as f64, + y: i as f64, + z: i as f64, + }, + ); + world.attach_component( + entity, + Velocity { + x: i as f64 * 0.1, + y: i as f64 * 0.1, + z: i as f64 * 0.1, + }, + ); + } world.execute(print_system); world.execute(shuffle_system); @@ -27,6 +59,11 @@ fn main() { world.execute(shuffle_system); println!("Shuffled and incremented"); world.execute(print_system); + println!("==================="); + world.execute(print2_system); + println!("Applying velocity"); + world.execute(apply_velocity_system); + world.execute(print2_system); } fn print_system(iter: SingleComponentExclusiveIter<'_, Position>) { @@ -51,3 +88,20 @@ fn increment_system(iter: SingleComponentExclusiveIterMut<'_, Position>) { pos.z += 3.0; } } + +fn print2_system(iter: PairComponentsRefIter<'_, Position, Velocity>) { + for (pos, vel) in iter { + println!( + "Pos: [{}, {}, {}] Vel: [{}, {}, {}]", + pos.x, pos.y, pos.z, vel.x, vel.y, vel.z + ); + } +} + +fn apply_velocity_system(iter: PairComponentsRefIterMut<'_, Position, Velocity>) { + for (mut pos, vel) in iter { + pos.x += vel.x; + pos.y += vel.y; + pos.z += vel.z; + } +} diff --git a/src/ecs.rs b/src/ecs.rs index 5908fde..e68ddfc 100644 --- a/src/ecs.rs +++ b/src/ecs.rs @@ -5,8 +5,8 @@ mod world; pub use component::Component; pub use iter::{ - FromWorld, SingleComponentExclusiveIter, SingleComponentExclusiveIterMut, - SingleComponentRefIter, SingleComponentRefIterMut, + FromWorld, PairComponentsRefIter, PairComponentsRefIterMut, SingleComponentExclusiveIter, + SingleComponentExclusiveIterMut, SingleComponentRefIter, SingleComponentRefIterMut, }; pub use system::System; pub use world::{World, WorldBuilder}; diff --git a/src/ecs/iter.rs b/src/ecs/iter.rs index 23c9690..3cd8c03 100644 --- a/src/ecs/iter.rs +++ b/src/ecs/iter.rs @@ -177,3 +177,163 @@ where } } } + +pub struct PairComponentsRefIter<'world, C1, C2> +where + C1: Component, + C2: Component, +{ + slice1: Option]>>, + slice2: Option]>>, +} + +impl<'world, C1, C2> FromWorld<'world> for PairComponentsRefIter<'world, C1, C2> +where + C1: Component, + C2: Component, +{ + fn from_world(world: &'world mut World) -> Self { + let slice1 = world + .components + .borrow_slice::() + .expect("Component not registered"); + let slice2 = world + .components + .borrow_slice::() + .expect("Component not registered"); + Self { + slice1: Some(slice1), + slice2: Some(slice2), + } + } +} + +impl<'world, C1, C2> Iterator for PairComponentsRefIter<'world, C1, C2> +where + C1: Component, + C2: Component, +{ + type Item = (Ref<'world, C1>, Ref<'world, C2>); + fn next(&mut self) -> Option { + loop { + let next1 = match self.slice1.take() { + Some(borrow) => match *borrow { + [] => return None, + [_, ..] => { + let (head, tail) = Ref::map_split(borrow, |slice| (&slice[0], &slice[1..])); + self.slice1.replace(tail); + head + } + }, + None => return None, + }; + let next2 = match self.slice2.take() { + Some(borrow) => match *borrow { + [] => return None, + [_, ..] => { + let (head, tail) = Ref::map_split(borrow, |slice| (&slice[0], &slice[1..])); + self.slice2.replace(tail); + head + } + }, + None => return None, + }; + if next1.is_some() && next2.is_some() { + let c1 = Ref::map(next1, |v| { + // SAFETY: next1 is Some + unsafe { v.as_ref().unwrap_unchecked() } + }); + let c2 = Ref::map(next2, |v| { + // SAFETY: next2 is Some + unsafe { v.as_ref().unwrap_unchecked() } + }); + return Some((c1, c2)); + } else { + continue; + } + } + } +} + +pub struct PairComponentsRefIterMut<'world, C1, C2> +where + C1: Component, + C2: Component, +{ + slice1: Option]>>, + slice2: Option]>>, +} + +impl<'world, C1, C2> FromWorld<'world> for PairComponentsRefIterMut<'world, C1, C2> +where + C1: Component, + C2: Component, +{ + fn from_world(world: &'world mut World) -> Self { + let slice1 = world + .components + .borrow_mut_slice::() + .expect("Component not registered"); + let slice2 = world + .components + .borrow_mut_slice::() + .expect("Component not registered"); + Self { + slice1: Some(slice1), + slice2: Some(slice2), + } + } +} + +impl<'world, C1, C2> Iterator for PairComponentsRefIterMut<'world, C1, C2> +where + C1: Component, + C2: Component, +{ + type Item = (RefMut<'world, C1>, RefMut<'world, C2>); + fn next(&mut self) -> Option { + loop { + let next1 = match self.slice1.take() { + Some(borrow) => match *borrow { + [] => return None, + [_, ..] => { + let (head, tail) = RefMut::map_split(borrow, |slice| { + let (left, right) = slice.split_at_mut(1); + (&mut left[0], right) + }); + self.slice1.replace(tail); + head + } + }, + None => return None, + }; + let next2 = match self.slice2.take() { + Some(borrow) => match *borrow { + [] => return None, + [_, ..] => { + let (head, tail) = RefMut::map_split(borrow, |slice| { + let (left, right) = slice.split_at_mut(1); + (&mut left[0], right) + }); + self.slice2.replace(tail); + head + } + }, + None => return None, + }; + if next1.is_some() && next2.is_some() { + let c1 = RefMut::map(next1, |v| { + // SAFETY: next1 is Some + unsafe { v.as_mut().unwrap_unchecked() } + }); + let c2 = RefMut::map(next2, |v| { + // SAFETY: next2 is Some + unsafe { v.as_mut().unwrap_unchecked() } + }); + return Some((c1, c2)); + } else { + continue; + } + } + } +} From e6e73699d3a69c5729b2d8405bad7b24adb2ae15 Mon Sep 17 00:00:00 2001 From: yuma14 Date: Mon, 13 May 2024 22:57:37 +0900 Subject: [PATCH 2/2] doc: update --- README.md | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++---- src/lib.rs | 58 +++++++++++++++++++++++++++++++-- 2 files changed, 146 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 925004e..84645e8 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,15 @@ A toy ECS library; works on Windows, macOS, Linux and WebAssembly. ## Benchmark -[Benchmark](./doc/benchmark.md) +[Benchmark result](./doc/benchmark.md) ## Usage ```rust -use xanadu::ecs::{SingleComponentIter, SingleComponentIterMut, World}; +use xanadu::ecs::{ + PairComponentsRefIter, PairComponentsRefIterMut, SingleComponentExclusiveIter, + SingleComponentExclusiveIterMut, World, +}; #[derive(Debug)] pub struct Position { @@ -22,8 +25,18 @@ pub struct Position { pub z: f64, } +#[derive(Debug)] +pub struct Velocity { + pub x: f64, + pub y: f64, + pub z: f64, +} + fn main() { - let mut world = World::builder().register_component::().build(); + let mut world = World::builder() + .register_component::() + .register_component::() + .build(); for i in 0..5 { let entity = world.new_entity(); world.attach_component( @@ -35,6 +48,25 @@ fn main() { }, ); } + for i in 0..3 { + let entity = world.new_entity(); + world.attach_component( + entity, + Position { + x: i as f64, + y: i as f64, + z: i as f64, + }, + ); + world.attach_component( + entity, + Velocity { + x: i as f64 * 0.1, + y: i as f64 * 0.1, + z: i as f64 * 0.1, + }, + ); + } world.execute(print_system); world.execute(shuffle_system); @@ -42,15 +74,20 @@ fn main() { world.execute(shuffle_system); println!("Shuffled and incremented"); world.execute(print_system); + println!("==================="); + world.execute(print2_system); + println!("Applying velocity"); + world.execute(apply_velocity_system); + world.execute(print2_system); } -fn print_system(iter: SingleComponentIter<'_, Position>) { +fn print_system(iter: SingleComponentExclusiveIter<'_, Position>) { for pos in iter { println!("Pos: [{}, {}, {}]", pos.x, pos.y, pos.z); } } -fn shuffle_system(iter: SingleComponentIterMut<'_, Position>) { +fn shuffle_system(iter: SingleComponentExclusiveIterMut<'_, Position>) { for pos in iter { let tmp = pos.x; pos.x = pos.y; @@ -59,13 +96,60 @@ fn shuffle_system(iter: SingleComponentIterMut<'_, Position>) { } } -fn increment_system(iter: SingleComponentIterMut<'_, Position>) { +fn increment_system(iter: SingleComponentExclusiveIterMut<'_, Position>) { for pos in iter { pos.x += 1.0; pos.y += 2.0; pos.z += 3.0; } } + +fn print2_system(iter: PairComponentsRefIter<'_, Position, Velocity>) { + for (pos, vel) in iter { + println!( + "Pos: [{}, {}, {}] Vel: [{}, {}, {}]", + pos.x, pos.y, pos.z, vel.x, vel.y, vel.z + ); + } +} + +fn apply_velocity_system(iter: PairComponentsRefIterMut<'_, Position, Velocity>) { + for (mut pos, vel) in iter { + pos.x += vel.x; + pos.y += vel.y; + pos.z += vel.z; + } +} +``` + +Result: + +``` +Pos: [0, 0, 0] +Pos: [1, 1, 1] +Pos: [2, 2, 2] +Pos: [3, 3, 3] +Pos: [4, 4, 4] +Pos: [0, 0, 0] +Pos: [1, 1, 1] +Pos: [2, 2, 2] +Shuffled and incremented +Pos: [2, 3, 1] +Pos: [3, 4, 2] +Pos: [4, 5, 3] +Pos: [5, 6, 4] +Pos: [6, 7, 5] +Pos: [2, 3, 1] +Pos: [3, 4, 2] +Pos: [4, 5, 3] +=================== +Pos: [2, 3, 1] Vel: [0, 0, 0] +Pos: [3, 4, 2] Vel: [0.1, 0.1, 0.1] +Pos: [4, 5, 3] Vel: [0.2, 0.2, 0.2] +Applying velocity +Pos: [2, 3, 1] Vel: [0, 0, 0] +Pos: [3.1, 4.1, 2.1] Vel: [0.1, 0.1, 0.1] +Pos: [4.2, 5.2, 3.2] Vel: [0.2, 0.2, 0.2] ``` ## Tests diff --git a/src/lib.rs b/src/lib.rs index b57a05e..b377225 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,10 @@ //! ## Example //! //! ```rust -//! use xanadu::ecs::{SingleComponentExclusiveIter, SingleComponentExclusiveIterMut, World}; +//! use xanadu::ecs::{ +//! PairComponentsRefIter, PairComponentsRefIterMut, SingleComponentExclusiveIter, +//! SingleComponentExclusiveIterMut, World, +//! }; //! //! #[derive(Debug)] //! pub struct Position { @@ -14,8 +17,18 @@ //! pub z: f64, //! } //! +//! #[derive(Debug)] +//! pub struct Velocity { +//! pub x: f64, +//! pub y: f64, +//! pub z: f64, +//! } +//! //! fn main() { -//! let mut world = World::builder().register_component::().build(); +//! let mut world = World::builder() +//! .register_component::() +//! .register_component::() +//! .build(); //! for i in 0..5 { //! let entity = world.new_entity(); //! world.attach_component( @@ -27,6 +40,25 @@ //! }, //! ); //! } +//! for i in 0..3 { +//! let entity = world.new_entity(); +//! world.attach_component( +//! entity, +//! Position { +//! x: i as f64, +//! y: i as f64, +//! z: i as f64, +//! }, +//! ); +//! world.attach_component( +//! entity, +//! Velocity { +//! x: i as f64 * 0.1, +//! y: i as f64 * 0.1, +//! z: i as f64 * 0.1, +//! }, +//! ); +//! } //! //! world.execute(print_system); //! world.execute(shuffle_system); @@ -34,6 +66,11 @@ //! world.execute(shuffle_system); //! println!("Shuffled and incremented"); //! world.execute(print_system); +//! println!("==================="); +//! world.execute(print2_system); +//! println!("Applying velocity"); +//! world.execute(apply_velocity_system); +//! world.execute(print2_system); //! } //! //! fn print_system(iter: SingleComponentExclusiveIter<'_, Position>) { @@ -58,6 +95,23 @@ //! pos.z += 3.0; //! } //! } +//! +//! fn print2_system(iter: PairComponentsRefIter<'_, Position, Velocity>) { +//! for (pos, vel) in iter { +//! println!( +//! "Pos: [{}, {}, {}] Vel: [{}, {}, {}]", +//! pos.x, pos.y, pos.z, vel.x, vel.y, vel.z +//! ); +//! } +//! } +//! +//! fn apply_velocity_system(iter: PairComponentsRefIterMut<'_, Position, Velocity>) { +//! for (mut pos, vel) in iter { +//! pos.x += vel.x; +//! pos.y += vel.y; +//! pos.z += vel.z; +//! } +//! } //! ``` /// Collections to be used in ECS, but can be used independently.