From 50d1ee6035737bcddedd013b91e10ec880b249ce Mon Sep 17 00:00:00 2001 From: Sage Vaillancourt Date: Tue, 25 Mar 2025 15:49:18 -0400 Subject: [PATCH] More strongly-typed Filters Requires some very repetitive constructs (no variable-count generics) but simplifies filter iteration for the simplest cases. --- src/Filter.cs | 161 ++++++++++++++++++++++++++++++++++++++++++- src/FilterBuilder.cs | 18 ++++- 2 files changed, 175 insertions(+), 4 deletions(-) diff --git a/src/Filter.cs b/src/Filter.cs index 2fd2f19..4b1cc8c 100644 --- a/src/Filter.cs +++ b/src/Filter.cs @@ -1,11 +1,166 @@ using System; +using System.Collections; +using System.Collections.Generic; using MoonTools.ECS.Collections; namespace MoonTools.ECS; +public class Filter where T : unmanaged +{ + private readonly Filter _underlying; + + internal Filter(Filter underlying) + { + _underlying = underlying; + } + + public TypedEntityEnum GetEnumerator() + { + return new TypedEntityEnum(_underlying.Entities, _underlying.World); + } +} + +public ref struct TypedEntityEnum : IEnumerator<(Entity entity, T)> where T : unmanaged +{ + private ReverseSpanEnumerator _underlying; + private readonly World _world; + + public TypedEntityEnum(ReverseSpanEnumerator underlying, World world) + { + _underlying = underlying; + _world = world; + } + + public bool MoveNext() + { + if (!_underlying.MoveNext()) + { + return false; + } + Entity currentEntity = _underlying.Current; + Current = (currentEntity, _world.Get(currentEntity)); + return true; + } + + public void Reset() + { + throw new NotImplementedException(); + } + + public (Entity, T) Current { get; private set; } + + object IEnumerator.Current => Current; + + public void Dispose() + { + } +} + +public class Filter where T1 : unmanaged where T2 : unmanaged +{ + private readonly Filter _underlying; + + internal Filter(Filter underlying) + { + _underlying = underlying; + } + + public TypedEntityEnum GetEnumerator() + { + return new TypedEntityEnum(_underlying.Entities, _underlying.World); + } +} + +public ref struct TypedEntityEnum : IEnumerator<(Entity entity, T1, T2)> where T1 : unmanaged where T2 : unmanaged +{ + private ReverseSpanEnumerator _underlying; + private readonly World _world; + + public TypedEntityEnum(ReverseSpanEnumerator underlying, World world) + { + _underlying = underlying; + _world = world; + } + + public bool MoveNext() + { + if (!_underlying.MoveNext()) + { + return false; + } + Entity currentEntity = _underlying.Current; + Current = (currentEntity, _world.Get(currentEntity), _world.Get(currentEntity)); + return true; + } + + public void Reset() + { + throw new NotImplementedException(); + } + + public (Entity, T1, T2) Current { get; private set; } + + object IEnumerator.Current => Current; + + public void Dispose() + { + } +} + +public class Filter where T1 : unmanaged where T2 : unmanaged where T3 : unmanaged +{ + private readonly Filter _underlying; + + internal Filter(Filter underlying) + { + _underlying = underlying; + } + + public TypedEntityEnum GetEnumerator() + { + return new TypedEntityEnum(_underlying.Entities, _underlying.World); + } +} + +public ref struct TypedEntityEnum : IEnumerator<(Entity entity, T1, T2, T3)> where T1 : unmanaged where T2 : unmanaged where T3 : unmanaged +{ + private ReverseSpanEnumerator _underlying; + private readonly World _world; + + public TypedEntityEnum(ReverseSpanEnumerator underlying, World world) + { + _underlying = underlying; + _world = world; + } + + public bool MoveNext() + { + if (!_underlying.MoveNext()) + { + return false; + } + Entity currentEntity = _underlying.Current; + Current = (currentEntity, _world.Get(currentEntity), _world.Get(currentEntity), _world.Get(currentEntity)); + return true; + } + + public void Reset() + { + throw new NotImplementedException(); + } + + public (Entity, T1, T2, T3) Current { get; private set; } + + object IEnumerator.Current => Current; + + public void Dispose() + { + } +} + public class Filter { - private World World; + internal World World; internal FilterSignature Signature; internal IndexableSet EntitySet = new IndexableSet(); @@ -17,10 +172,10 @@ public class Filter public bool Empty => EntitySet.Count == 0; public int Count => EntitySet.Count; - // WARNING: this WILL crash if the index is out of range! + /// WARNING: this WILL crash if the index is out of range! public Entity NthEntity(int index) => EntitySet[index]; - // WARNING: this WILL crash if the filter is empty! + /// WARNING: this WILL crash if the filter is empty! public Entity RandomEntity => EntitySet[RandomManager.Next(EntitySet.Count)]; public RandomEntityEnumerator EntitiesInRandomOrder => new RandomEntityEnumerator(this); diff --git a/src/FilterBuilder.cs b/src/FilterBuilder.cs index 3e3ef6d..1d075dd 100644 --- a/src/FilterBuilder.cs +++ b/src/FilterBuilder.cs @@ -1,4 +1,5 @@ -using MoonTools.ECS.Collections; +using System; +using MoonTools.ECS.Collections; namespace MoonTools.ECS { @@ -39,5 +40,20 @@ namespace MoonTools.ECS var signature = new FilterSignature(Included, Excluded); return World.GetFilter(signature); } + + public Filter Of() where T : unmanaged + { + return new Filter(Include().Build()); + } + + public Filter Of() where T1 : unmanaged where T2 : unmanaged + { + return new Filter(Include().Include().Build()); + } + + public Filter Of() where T1 : unmanaged where T2 : unmanaged where T3 : unmanaged + { + return new Filter(Include().Include().Include().Build()); + } } }