From 99ea5727ca4638efd2979218a128e56c0ce32c44 Mon Sep 17 00:00:00 2001 From: HampusM Date: Thu, 29 Feb 2024 19:57:59 +0100 Subject: feat(ecs): add iterating over queries non-mutably --- ecs/examples/multiple_queries.rs | 8 +-- ecs/examples/simple.rs | 4 +- ecs/examples/with_local.rs | 8 +-- ecs/src/component.rs | 42 +++++++++++-- ecs/src/lib.rs | 130 +++++++++++++++++++++++++++++++-------- 5 files changed, 153 insertions(+), 39 deletions(-) diff --git a/ecs/examples/multiple_queries.rs b/ecs/examples/multiple_queries.rs index 4f4198a..9a699c2 100644 --- a/ecs/examples/multiple_queries.rs +++ b/ecs/examples/multiple_queries.rs @@ -29,12 +29,12 @@ impl Display for EnemyName } } -fn say_hello( - mut query: Query<(AttackStrength,)>, +fn do_attacks( + attacker_query: Query<(AttackStrength,)>, mut enemy_query: Query<(Health, EnemyName)>, ) { - for (attack_strength,) in query.iter_mut() { + for (attack_strength,) in &attacker_query { for (health, enemy_name) in enemy_query.iter_mut() { let damage = match attack_strength { AttackStrength::Strong => 20, @@ -66,7 +66,7 @@ fn main() { let mut world = World::::new(); - world.register_system(Event::Start, say_hello); + world.register_system(Event::Start, do_attacks); world.create_entity(( Health { health: 100 }, diff --git a/ecs/examples/simple.rs b/ecs/examples/simple.rs index b8dc50f..76d9538 100644 --- a/ecs/examples/simple.rs +++ b/ecs/examples/simple.rs @@ -12,9 +12,9 @@ struct Greeting greeting: String, } -fn say_hello(mut query: Query<(SomeData, Greeting)>) +fn say_hello(query: Query<(SomeData, Greeting)>) { - for (data, greeting) in query.iter_mut() { + for (data, greeting) in &query { println!("{}: {}", greeting.greeting, data.num); } } diff --git a/ecs/examples/with_local.rs b/ecs/examples/with_local.rs index 334f129..b37a5c3 100644 --- a/ecs/examples/with_local.rs +++ b/ecs/examples/with_local.rs @@ -20,18 +20,18 @@ struct SayHelloState cnt: usize, } -fn say_hello(mut query: Query<(SomeData,)>, mut state: Local) +fn say_hello(query: Query<(SomeData,)>, mut state: Local) { - for (data,) in query.iter_mut() { + for (data,) in &query { println!("Hello there. Count {}: {}", state.cnt, data.num); state.cnt += 1; } } -fn say_whats_up(mut query: Query<(SomeData, Name)>, mut state: Local) +fn say_whats_up(query: Query<(SomeData, Name)>, mut state: Local) { - for (data, name) in query.iter_mut() { + for (data, name) in &query { println!( "Whats up, {}. Number is {}. Count {}", name.name, data.num, state.cnt diff --git a/ecs/src/component.rs b/ecs/src/component.rs index 70ce9ba..023be86 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -23,6 +23,11 @@ impl dyn Component self.as_any_mut().downcast_mut() } + pub fn downcast_ref(&self) -> Option<&Real> + { + self.as_any().downcast_ref() + } + pub fn is(&self) -> bool { self.as_any().is::() @@ -40,6 +45,10 @@ impl Debug for dyn Component /// A sequence of components. pub trait Sequence { + type Refs<'component> + where + Self: 'component; + type MutRefs<'component> where Self: 'component; @@ -48,16 +57,22 @@ pub trait Sequence fn type_ids() -> Vec; - fn from_components(components: &mut [Box]) -> Self::MutRefs<'_>; + fn from_components(components: &[Box]) -> Self::Refs<'_>; + + fn from_components_mut(components: &mut [Box]) -> Self::MutRefs<'_>; } macro_rules! inner { ($c: tt) => { seq!(I in 0..=$c { impl<#(Comp~I: Component,)*> Sequence for (#(Comp~I,)*) { + type Refs<'component> = (#(&'component Comp~I,)*) + where Self: 'component; + type MutRefs<'component> = (#(&'component mut Comp~I,)*) where Self: 'component; + fn into_vec(self) -> Vec> { Vec::from_iter([#(Box::new(self.I) as Box,)*]) } @@ -70,7 +85,28 @@ macro_rules! inner { ] } - fn from_components( + fn from_components(components: &[Box]) -> Self::Refs<'_> + { + #( + let mut comp_~I = None; + )* + + for comp in components { + #( + if comp.is::() { + comp_~I = Some(comp); + continue; + } + )* + } + + (#( + comp_~I.unwrap().downcast_ref::().unwrap(), + )*) + + } + + fn from_components_mut( components: &mut [Box], ) -> Self::MutRefs<'_> { @@ -84,11 +120,9 @@ macro_rules! inner { comp_~I = Some(comp); continue; } - )* } - (#( comp_~I.unwrap().downcast_mut::().unwrap(), )*) diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 4e3e4a1..3c59554 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -5,7 +5,7 @@ use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; -use std::slice::IterMut as SliceIterMut; +use std::slice::{Iter as SliceIter, IterMut as SliceIterMut}; use crate::component::{Component, Sequence as ComponentSequence}; use crate::system::{ @@ -128,9 +128,18 @@ impl<'world, Comps> Query<'world, Comps> where Comps: ComponentSequence, { - pub fn iter_mut(&mut self) -> QueryComponentIter + pub fn iter(&self) -> QueryComponentIter { QueryComponentIter { + entity_iter: self.component_storage.entities.iter(), + component_type_ids: Comps::type_ids(), + comps_pd: PhantomData, + } + } + + pub fn iter_mut(&mut self) -> QueryComponentMutIter + { + QueryComponentMutIter { entity_iter: self.component_storage.entities.iter_mut(), component_type_ids: Comps::type_ids(), comps_pd: PhantomData, @@ -138,11 +147,24 @@ where } } -impl<'world, Comps> IntoIterator for &'world mut Query<'world, Comps> +impl<'world, Comps> IntoIterator for &'world Query<'world, Comps> where - Comps: ComponentSequence + 'world, + Comps: ComponentSequence, { type IntoIter = QueryComponentIter<'world, Comps>; + type Item = Comps::Refs<'world>; + + fn into_iter(self) -> Self::IntoIter + { + self.iter() + } +} + +impl<'world, Comps> IntoIterator for &'world mut Query<'world, Comps> +where + Comps: ComponentSequence, +{ + type IntoIter = QueryComponentMutIter<'world, Comps>; type Item = Comps::MutRefs<'world>; fn into_iter(self) -> Self::IntoIter @@ -217,12 +239,36 @@ impl QueryComponentIds pub struct QueryComponentIter<'world, Comps> { - entity_iter: SliceIterMut<'world, Entity>, + entity_iter: SliceIter<'world, Entity>, component_type_ids: Vec, comps_pd: PhantomData, } impl<'world, Comps> Iterator for QueryComponentIter<'world, Comps> +where + Comps: ComponentSequence + 'world, +{ + type Item = Comps::Refs<'world>; + + fn next(&mut self) -> Option + { + let matching_entity = find_entity_with_components::<&Entity>( + &mut self.entity_iter, + &self.component_type_ids, + )?; + + Some(Comps::from_components(&matching_entity.components)) + } +} + +pub struct QueryComponentMutIter<'world, Comps> +{ + entity_iter: SliceIterMut<'world, Entity>, + component_type_ids: Vec, + comps_pd: PhantomData, +} + +impl<'world, Comps> Iterator for QueryComponentMutIter<'world, Comps> where Comps: ComponentSequence + 'world, { @@ -230,29 +276,63 @@ where fn next(&mut self) -> Option { - // TODO: This is a really dumb and slow way to do this. Refactor the world - // to store components in archetypes - let entity = - self.entity_iter.find(|entity| { - let entity_components: HashSet<_> = entity - .components - .iter() - .map(|component| component.as_ref().type_id()) - .collect(); - - if self.component_type_ids.iter().all(|component_type_id| { - entity_components.contains(component_type_id) - }) { - return true; - } - - false - })?; - - Some(Comps::from_components(&mut entity.components)) + let matching_entity = find_entity_with_components::<&mut Entity>( + &mut self.entity_iter, + &self.component_type_ids, + )?; + + Some(Comps::from_components_mut(&mut matching_entity.components)) + } +} + +trait EntityRef +{ + fn components(&self) -> &[Box]; +} + +impl EntityRef for &Entity +{ + fn components(&self) -> &[Box] + { + &self.components } } +impl EntityRef for &mut Entity +{ + fn components(&self) -> &[Box] + { + &self.components + } +} + +fn find_entity_with_components( + entity_iter: &mut impl Iterator, + component_type_ids: &[TypeId], +) -> Option +where + EntityRefT: EntityRef, +{ + // TODO: This is a really dumb and slow way to do this. Refactor the world + // to store components in archetypes + entity_iter.find(|entity| { + let entity_components: HashSet<_> = entity + .components() + .iter() + .map(|component| component.as_ref().type_id()) + .collect(); + + if component_type_ids + .iter() + .all(|component_type_id| entity_components.contains(component_type_id)) + { + return true; + } + + false + }) +} + #[derive(Debug)] pub struct ComponentStorage { -- cgit v1.2.3-18-g5258