summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2024-02-29 19:57:59 +0100
committerHampusM <hampus@hampusmat.com>2024-02-29 19:58:10 +0100
commit99ea5727ca4638efd2979218a128e56c0ce32c44 (patch)
treef167d9d88ad8b4cfe7089df3cc67e1be9b0f9a06
parentbd627c91819ea98e551b29027de6eaaccbe45ed6 (diff)
feat(ecs): add iterating over queries non-mutably
-rw-r--r--ecs/examples/multiple_queries.rs8
-rw-r--r--ecs/examples/simple.rs4
-rw-r--r--ecs/examples/with_local.rs8
-rw-r--r--ecs/src/component.rs42
-rw-r--r--ecs/src/lib.rs130
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::<Event>::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<SayHelloState>)
+fn say_hello(query: Query<(SomeData,)>, mut state: Local<SayHelloState>)
{
- 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<SayHelloState>)
+fn say_whats_up(query: Query<(SomeData, Name)>, mut state: Local<SayHelloState>)
{
- 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<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
{