ecs: implement an actual Filter trait, create a Changed filter
This commit is contained in:
parent
2107b8f7b0
commit
782d64f6cf
|
@ -1,3 +1,5 @@
|
|||
#![feature(associated_type_defaults)]
|
||||
|
||||
extern crate self as lyra_ecs;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use crate::{query::{AsFilter, AsQuery, Fetch, Filter, Query}, Component, ComponentColumn, DynTypeId, Tick, World};
|
||||
|
||||
pub struct ChangedFetcher<'a, T> {
|
||||
col: &'a ComponentColumn,
|
||||
tick: Tick,
|
||||
_phantom: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
impl<'a, T> Fetch<'a> for ChangedFetcher<'a, T>
|
||||
where
|
||||
T: 'a,
|
||||
{
|
||||
type Item = bool;
|
||||
|
||||
fn dangling() -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
unsafe fn get_item(&mut self, entity: crate::world::ArchetypeEntityId) -> Self::Item {
|
||||
let tick = self.col.entity_ticks[entity.0 as usize];
|
||||
tick >= self.tick
|
||||
}
|
||||
}
|
||||
|
||||
/// A filter that fetches components that have changed.
|
||||
///
|
||||
/// Since [`AsQuery`] is implemented for `&T`, you can use this query like this:
|
||||
/// ```nobuild
|
||||
/// for ts in world.view::<&T>() {
|
||||
/// println!("Got a &T!");
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Changed<T> {
|
||||
type_id: DynTypeId,
|
||||
_phantom: PhantomData<T>
|
||||
}
|
||||
|
||||
impl<T: Component> Default for Changed<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
type_id: DynTypeId::of::<T>(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// manually implemented to avoid a Copy bound on T
|
||||
impl<T> Copy for Changed<T> {}
|
||||
|
||||
// manually implemented to avoid a Clone bound on T
|
||||
impl<T> Clone for Changed<T> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Component> Changed<T> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Component> Query for Changed<T>
|
||||
where
|
||||
T: 'static
|
||||
{
|
||||
type Item<'a> = bool;
|
||||
type Fetch<'a> = ChangedFetcher<'a, T>;
|
||||
|
||||
fn new() -> Self {
|
||||
Changed::<T>::new()
|
||||
}
|
||||
|
||||
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
|
||||
archetype.has_column(self.type_id)
|
||||
}
|
||||
|
||||
unsafe fn fetch<'a>(&self, w: &'a World, a: &'a crate::archetype::Archetype, _: crate::Tick) -> Self::Fetch<'a> {
|
||||
ChangedFetcher {
|
||||
col: a.get_column(self.type_id).unwrap(),
|
||||
tick: w.current_tick(),
|
||||
_phantom: PhantomData::<&T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Component> AsQuery for Changed<T> {
|
||||
type Query = Self;
|
||||
}
|
||||
|
||||
impl<'a, T: Component> Filter for Changed<T> { }
|
||||
|
||||
impl<T: Component> AsFilter for Changed<T> {
|
||||
type Filter = Self;
|
||||
}
|
|
@ -5,4 +5,7 @@ mod or;
|
|||
pub use or::*;
|
||||
|
||||
mod not;
|
||||
pub use not::*;
|
||||
pub use not::*;
|
||||
|
||||
mod changed;
|
||||
pub use changed::*;
|
|
@ -89,6 +89,12 @@ pub trait AsQuery {
|
|||
type Query: Query;
|
||||
}
|
||||
|
||||
/// A trait for getting the filter of a type.
|
||||
pub trait AsFilter {
|
||||
/// The query for this type
|
||||
type Filter: Filter;
|
||||
}
|
||||
|
||||
pub trait IntoQuery {
|
||||
fn into_query(self) -> Self;
|
||||
}
|
||||
|
@ -125,10 +131,22 @@ impl Query for () {
|
|||
}
|
||||
}
|
||||
|
||||
impl Filter for () {
|
||||
type Item<'a> = bool;
|
||||
}
|
||||
|
||||
impl AsQuery for () {
|
||||
type Query = ();
|
||||
}
|
||||
|
||||
pub trait Filter: Query {
|
||||
type Item<'a> = bool;
|
||||
}
|
||||
|
||||
impl AsFilter for () {
|
||||
type Filter = ();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{World, archetype::Archetype, tests::Vec2};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::World;
|
||||
|
||||
use super::{Query, Fetch, AsQuery};
|
||||
use super::{Query, Fetch, AsQuery, Filter, AsFilter};
|
||||
|
||||
impl<'a, F1> Fetch<'a> for (F1,)
|
||||
/* impl<'a, F1> Fetch<'a> for (F1,)
|
||||
where
|
||||
F1: Fetch<'a>,
|
||||
{
|
||||
|
@ -102,7 +102,7 @@ where
|
|||
Q2: AsQuery,
|
||||
{
|
||||
type Query = (Q1::Query, Q2::Query);
|
||||
}
|
||||
} */
|
||||
|
||||
macro_rules! impl_bundle_tuple {
|
||||
( $($name: ident),+ ) => (
|
||||
|
@ -154,10 +154,39 @@ macro_rules! impl_bundle_tuple {
|
|||
impl<$($name: AsQuery),+> AsQuery for ($($name,)+) {
|
||||
type Query = ($($name::Query,)+);
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl<$($name: Filter),+> Filter for ($($name,)+) {
|
||||
//type Item<'a> = ($(<$name as Query>::Item<'a>,)+);
|
||||
//type Fetch<'a> = ($(<$name as Query>::Fetch<'a>,)+);
|
||||
|
||||
/* fn new() -> Self {
|
||||
( $($name::new(),)+ )
|
||||
}
|
||||
|
||||
fn can_visit_archetype(&self, archetype: &crate::archetype::Archetype) -> bool {
|
||||
let ( $($name,)+ ) = self;
|
||||
|
||||
// this is the only way I could figure out how to do an 'and'
|
||||
let bools = vec![$($name.can_visit_archetype(archetype),)+];
|
||||
bools.iter().all(|b| *b)
|
||||
}
|
||||
|
||||
unsafe fn fetch<'a>(&self, world: &'a World, archetype: &'a crate::archetype::Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
|
||||
let ( $($name,)+ ) = self;
|
||||
( $($name.fetch(world, archetype, tick),)+ )
|
||||
} */
|
||||
}
|
||||
|
||||
impl<$($name: AsFilter),+> AsFilter for ($($name,)+) {
|
||||
type Filter = ($($name::Filter,)+);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Hopefully up to 16 queries in a SINGLE view is enough
|
||||
impl_bundle_tuple! { Q1 }
|
||||
impl_bundle_tuple! { Q1, Q2 }
|
||||
impl_bundle_tuple! { Q1, Q2, Q3 }
|
||||
impl_bundle_tuple! { Q1, Q2, Q3, Q4 }
|
||||
impl_bundle_tuple! { Q1, Q2, Q3, Q4, Q5 }
|
||||
|
|
|
@ -2,11 +2,11 @@ use std::ops::Range;
|
|||
|
||||
use crate::{archetype::Archetype, world::{ArchetypeEntityId, World}, EntityId, Tick};
|
||||
|
||||
use super::{Query, Fetch, AsQuery};
|
||||
use super::{AsFilter, AsQuery, Fetch, Filter, Query};
|
||||
|
||||
pub type View<'a, Q, F = ()> = ViewState<'a, <Q as AsQuery>::Query, <F as AsQuery>::Query>;
|
||||
|
||||
pub struct ViewState<'a, Q: Query, F: Query> {
|
||||
pub struct ViewState<'a, Q: Query, F: Filter> {
|
||||
world: &'a World,
|
||||
query: Q,
|
||||
filter: F,
|
||||
|
@ -16,7 +16,7 @@ pub struct ViewState<'a, Q: Query, F: Query> {
|
|||
impl<'a, Q, F> ViewState<'a, Q, F>
|
||||
where
|
||||
Q: Query,
|
||||
F: Query,
|
||||
F: Filter,
|
||||
{
|
||||
pub fn new(world: &'a World, query: Q, filter: F, archetypes: Vec<&'a Archetype>) -> Self {
|
||||
Self {
|
||||
|
@ -38,7 +38,7 @@ where
|
|||
}
|
||||
|
||||
/// Consumes `self`, adding a filter to the view.
|
||||
pub fn with<U: AsQuery>(self, filter: U::Query) -> ViewState<'a, Q, (F, U::Query)> {
|
||||
pub fn with<U: AsFilter>(self, filter: U::Filter) -> ViewState<'a, Q, (F, U::Filter)> {
|
||||
ViewState::new(self.world, self.query, (self.filter, filter), self.archetypes)
|
||||
}
|
||||
}
|
||||
|
@ -46,18 +46,19 @@ where
|
|||
impl<'a, Q, F> IntoIterator for ViewState<'a, Q, F>
|
||||
where
|
||||
Q: Query,
|
||||
F: Query,
|
||||
F: Filter,
|
||||
{
|
||||
type Item = Q::Item<'a>;
|
||||
|
||||
type IntoIter = ViewIter<'a, Q, F>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
let tick = self.world.tick_tracker().tick_when(Q::MUTATES);
|
||||
//let tick = self.world.tick_tracker().tick_when(Q::MUTATES);
|
||||
|
||||
ViewIter {
|
||||
world: self.world,
|
||||
tick,
|
||||
tick: self.world.current_tick(),
|
||||
has_ticked: false,
|
||||
query: self.query,
|
||||
filter: self.filter,
|
||||
fetcher: None,
|
||||
|
@ -69,9 +70,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ViewIter<'a, Q: Query, F: Query> {
|
||||
pub struct ViewIter<'a, Q: Query, F: Filter> {
|
||||
world: &'a World,
|
||||
tick: Tick,
|
||||
has_ticked: bool,
|
||||
query: Q,
|
||||
filter: F,
|
||||
fetcher: Option<Q::Fetch<'a>>,
|
||||
|
@ -84,7 +86,7 @@ pub struct ViewIter<'a, Q: Query, F: Query> {
|
|||
impl<'a, Q, F> Iterator for ViewIter<'a, Q, F>
|
||||
where
|
||||
Q: Query,
|
||||
F: Query,
|
||||
F: Filter,
|
||||
{
|
||||
type Item = Q::Item<'a>;
|
||||
|
||||
|
@ -110,6 +112,13 @@ where
|
|||
let entity_index = ArchetypeEntityId(entity_index);
|
||||
|
||||
if fetcher.can_visit_item(entity_index) && filter_fetcher.can_visit_item(entity_index) {
|
||||
// only tick the world if the filter has fetched, and when the world hasn't
|
||||
// been ticked yet.
|
||||
if Q::MUTATES && !self.has_ticked {
|
||||
self.has_ticked = true;
|
||||
self.tick = self.world.tick();
|
||||
}
|
||||
|
||||
let i = unsafe { fetcher.get_item(entity_index) };
|
||||
return Some(i);
|
||||
}
|
||||
|
@ -147,17 +156,17 @@ pub struct ViewOne<'a, Q: Query> {
|
|||
|
||||
impl<'a, Q: Query> ViewOne<'a, Q> {
|
||||
pub fn new(world: &'a World, entity: EntityId, query: Q) -> Self {
|
||||
let tick = world.tick_tracker().tick_when(Q::MUTATES);
|
||||
//let tick = world.tick_tracker().tick_when(Q::MUTATES);
|
||||
|
||||
Self {
|
||||
world,
|
||||
tick,
|
||||
tick: world.current_tick(),
|
||||
entity,
|
||||
query
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Option<Q::Item<'a>> {
|
||||
pub fn get(self) -> Option<Q::Item<'a>> {
|
||||
if let Some(record) = self.world.entities.arch_index.get(&self.entity) {
|
||||
let arch = self.world.archetypes.get(&record.id)
|
||||
.expect("An invalid record was specified for an entity");
|
||||
|
@ -165,6 +174,9 @@ impl<'a, Q: Query> ViewOne<'a, Q> {
|
|||
if self.query.can_visit_archetype(arch) {
|
||||
let mut fetch = unsafe { self.query.fetch(self.world, arch, self.tick) };
|
||||
if fetch.can_visit_item(record.index) {
|
||||
// only tick the world when something is actually fetched.
|
||||
self.world.tick();
|
||||
|
||||
return Some(unsafe { fetch.get_item(record.index) });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::marker::PhantomData;
|
|||
|
||||
use lyra_ecs_derive::Component;
|
||||
|
||||
use crate::query::Filter;
|
||||
use crate::query::Query;
|
||||
use crate::query::ViewState;
|
||||
use crate::Entity;
|
||||
|
@ -98,7 +99,7 @@ impl World {
|
|||
impl<'a, Q, F> ViewState<'a, Q, F>
|
||||
where
|
||||
Q: Query,
|
||||
F: Query,
|
||||
F: Filter,
|
||||
{
|
||||
/// Consumes `self` to return a view that fetches the relation to a specific target entity.
|
||||
pub fn relates_to<R>(self, target: Entity) -> ViewState<'a, (Q, QueryRelatesTo<R>), F>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{any::Any, marker::PhantomData, ptr::NonNull};
|
||||
|
||||
use paste::paste;
|
||||
use crate::{World, Access, ResourceObject, query::{Query, ViewState, ResMut, Res}};
|
||||
use crate::{World, Access, ResourceObject, query::{Query, Filter, ViewState, ResMut, Res}};
|
||||
|
||||
use super::{System, IntoSystem};
|
||||
|
||||
|
@ -140,7 +140,7 @@ impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O, P }
|
|||
impl<'c, Q, F> FnArgFetcher for ViewState<'c, Q, F>
|
||||
where
|
||||
Q: Query + 'static,
|
||||
F: Query + 'static,
|
||||
F: Filter + 'static,
|
||||
{
|
||||
type State = (Q, F);
|
||||
type Arg<'a, 'state> = ViewState<'a, Q, F>;
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::{any::TypeId, collections::HashMap, ptr::NonNull};
|
|||
|
||||
use atomic_refcell::{AtomicRef, AtomicRefMut};
|
||||
|
||||
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, AsQuery, Query, ViewIter, ViewOne, ViewState}, resource::ResourceData, ComponentInfo, DynTypeId, DynamicBundle, Entities, Entity, ResourceObject, Tick, TickTracker};
|
||||
use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, AsFilter, AsQuery, Query, ViewIter, ViewOne, ViewState}, resource::ResourceData, ComponentInfo, DynTypeId, DynamicBundle, Entities, Entity, ResourceObject, Tick, TickTracker};
|
||||
|
||||
/// The id of the entity for the Archetype.
|
||||
///
|
||||
|
@ -347,9 +347,9 @@ impl World {
|
|||
}
|
||||
|
||||
/// View into the world for a set of entities that satisfy the query and the filter.
|
||||
pub fn filtered_view<Q: AsQuery, F: AsQuery>(&self) -> ViewState<Q::Query, F::Query> {
|
||||
pub fn filtered_view<Q: AsQuery, F: AsFilter>(&self) -> ViewState<Q::Query, F::Filter> {
|
||||
let archetypes = self.archetypes.values().collect();
|
||||
ViewState::<Q::Query, F::Query>::new(self, Q::Query::new(), F::Query::new(), archetypes)
|
||||
ViewState::<Q::Query, F::Filter>::new(self, Q::Query::new(), F::Filter::new(), archetypes)
|
||||
}
|
||||
|
||||
/// View into the world for a set of entities that satisfy the queries.
|
||||
|
@ -360,9 +360,9 @@ impl World {
|
|||
}
|
||||
|
||||
/// View into the world for a set of entities that satisfy the queries.
|
||||
pub fn filtered_view_iter<Q: AsQuery, F: AsQuery>(&self) -> ViewIter<Q::Query, F::Query> {
|
||||
pub fn filtered_view_iter<Q: AsQuery, F: AsFilter>(&self) -> ViewIter<Q::Query, F::Filter> {
|
||||
let archetypes = self.archetypes.values().collect();
|
||||
let v = ViewState::new(self, Q::Query::new(), F::Query::new(), archetypes);
|
||||
let v = ViewState::new(self, Q::Query::new(), F::Filter::new(), archetypes);
|
||||
v.into_iter()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue