diff options
Diffstat (limited to 'ecs/src')
-rw-r--r-- | ecs/src/component.rs | 42 | ||||
-rw-r--r-- | ecs/src/lib.rs | 130 |
2 files changed, 143 insertions, 29 deletions
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<Real: 'static>(&self) -> Option<&Real> + { + self.as_any().downcast_ref() + } + pub fn is<Other: 'static>(&self) -> bool { self.as_any().is::<Other>() @@ -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<TypeId>; - fn from_components(components: &mut [Box<dyn Component>]) -> Self::MutRefs<'_>; + fn from_components(components: &[Box<dyn Component>]) -> Self::Refs<'_>; + + fn from_components_mut(components: &mut [Box<dyn Component>]) -> 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<Box<dyn Component>> { Vec::from_iter([#(Box::new(self.I) as Box<dyn Component>,)*]) } @@ -70,7 +85,28 @@ macro_rules! inner { ] } - fn from_components( + fn from_components(components: &[Box<dyn Component>]) -> Self::Refs<'_> + { + #( + let mut comp_~I = None; + )* + + for comp in components { + #( + if comp.is::<Comp~I>() { + comp_~I = Some(comp); + continue; + } + )* + } + + (#( + comp_~I.unwrap().downcast_ref::<Comp~I>().unwrap(), + )*) + + } + + fn from_components_mut( components: &mut [Box<dyn Component>], ) -> Self::MutRefs<'_> { @@ -84,11 +120,9 @@ macro_rules! inner { comp_~I = Some(comp); continue; } - )* } - (#( comp_~I.unwrap().downcast_mut::<Comp~I>().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<Comps> + pub fn iter(&self) -> QueryComponentIter<Comps> { QueryComponentIter { + entity_iter: self.component_storage.entities.iter(), + component_type_ids: Comps::type_ids(), + comps_pd: PhantomData, + } + } + + pub fn iter_mut(&mut self) -> QueryComponentMutIter<Comps> + { + 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,7 +239,7 @@ impl QueryComponentIds pub struct QueryComponentIter<'world, Comps> { - entity_iter: SliceIterMut<'world, Entity>, + entity_iter: SliceIter<'world, Entity>, component_type_ids: Vec<TypeId>, comps_pd: PhantomData<Comps>, } @@ -226,33 +248,91 @@ impl<'world, Comps> Iterator for QueryComponentIter<'world, Comps> where Comps: ComponentSequence + 'world, { + type Item = Comps::Refs<'world>; + + fn next(&mut self) -> Option<Self::Item> + { + 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<TypeId>, + comps_pd: PhantomData<Comps>, +} + +impl<'world, Comps> Iterator for QueryComponentMutIter<'world, Comps> +where + Comps: ComponentSequence + 'world, +{ type Item = Comps::MutRefs<'world>; fn next(&mut self) -> Option<Self::Item> { - // 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<dyn Component>]; +} + +impl EntityRef for &Entity +{ + fn components(&self) -> &[Box<dyn Component>] + { + &self.components } } +impl EntityRef for &mut Entity +{ + fn components(&self) -> &[Box<dyn Component>] + { + &self.components + } +} + +fn find_entity_with_components<EntityRefT>( + entity_iter: &mut impl Iterator<Item = EntityRefT>, + component_type_ids: &[TypeId], +) -> Option<EntityRefT> +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 { |