diff --git a/samples/MyBattleground/Program.cs b/samples/MyBattleground/Program.cs index d7b87cf..a624bb1 100644 --- a/samples/MyBattleground/Program.cs +++ b/samples/MyBattleground/Program.cs @@ -8,6 +8,14 @@ using var ecs = new World(); +ecs.Entity() + .Set(new Position()) + .Set(new Velocity()); + +ecs.Entity() + .Set(new Position()) + .Set(new Velocity()); + for (int i = 0; i < ENTITIES_COUNT; i++) ecs.Entity() .Set(new Position()) @@ -15,6 +23,7 @@ .Set(); + ecs.Query() .With() .EachWithEntity(static (ref readonly EntityView e, ref Velocity pos, ref Position vel) => @@ -43,7 +52,7 @@ // pos.Y *= vel.Y; // }); - foreach (var archetype in query) + foreach (var archetype in ecs.Query<(Position, Velocity)>()) { var column0 = archetype.GetComponentIndex(); var column1 = archetype.GetComponentIndex(); @@ -65,6 +74,29 @@ } } } + + // foreach (var archetype in query) + // { + // var column0 = archetype.GetComponentIndex(); + // var column1 = archetype.GetComponentIndex(); + // + // foreach (ref readonly var chunk in archetype) + // { + // ref var pos = ref chunk.GetReference(column0); + // ref var vel = ref chunk.GetReference(column1); + // + // ref var last2 = ref Unsafe.Add(ref pos, chunk.Count); + // + // while (Unsafe.IsAddressLessThan(ref pos, ref last2)) + // { + // pos.X *= vel.X; + // pos.Y *= vel.Y; + // + // pos = ref Unsafe.Add(ref pos, 1); + // vel = ref Unsafe.Add(ref vel, 1); + // } + // } + // } } last = start; diff --git a/src/Commands.cs b/src/Commands.cs index 24af8f2..e6d86a5 100644 --- a/src/Commands.cs +++ b/src/Commands.cs @@ -38,21 +38,6 @@ public void Delete(EcsID id) } } - public unsafe void Set(EcsID id, EcsID tag) - { - // EcsAssert.Assert(!IDOp.IsPair(tag)); - // - // if (_main.Exists(tag) && Has(tag)) - // { - // ref readonly var cmp2 = ref _main.Component(); - // Set(id, in cmp2); - // return; - // } - // - // var cmp = new EcsComponent(tag, 0); - // Set(id, ref cmp); - } - public void Set(EcsID id) where T : struct { ref readonly var cmp = ref _main.Component(); @@ -180,7 +165,7 @@ public void Clear() _despawn.Clear(); } - private unsafe struct SetComponent + private struct SetComponent { public EcsID Entity; public int Component; diff --git a/src/DictionarySlim.cs b/src/DictionarySlim.cs index dd78ee5..ed52e82 100644 --- a/src/DictionarySlim.cs +++ b/src/DictionarySlim.cs @@ -215,9 +215,10 @@ public bool Remove(TKey key) /// /// Key to look for /// Reference to the new or existing value - public ref TValue GetOrAddValueRef(TKey key) + public ref TValue GetOrAddValueRef(TKey key, out bool exists) { if (key == null) ThrowHelper.ThrowKeyArgumentNullException(); + exists = true; Entry[] entries = _entries; int collisionCount = 0; int bucketIndex = key.GetHashCode() & (_buckets.Length - 1); @@ -235,6 +236,7 @@ public ref TValue GetOrAddValueRef(TKey key) collisionCount++; } + exists = false; return ref AddKey(key, bucketIndex); } diff --git a/src/EntityView.cs b/src/EntityView.cs index 277e807..b07c6a1 100644 --- a/src/EntityView.cs +++ b/src/EntityView.cs @@ -5,7 +5,7 @@ namespace TinyEcs; #endif [StructLayout(LayoutKind.Sequential)] [DebuggerDisplay("ID: {ID}")] -public readonly unsafe struct EntityView : IEquatable, IEquatable +public readonly struct EntityView : IEquatable, IEquatable { public static readonly EntityView Invalid = new(null, 0); @@ -35,36 +35,12 @@ public readonly EntityView Set() where T : struct return this; } - public readonly EntityView Set(EcsID id) - { - World.Set(ID, id); - return this; - } - public readonly EntityView Set(T component) where T : struct { World.Set(ID, component); return this; } - //public readonly EntityView Set() - // where TKind : unmanaged - // where TTarget : unmanaged - //{ - // return Set(World.Entity(), World.Entity()); - //} - - //public readonly EntityView Set(EcsID target) where TKind : unmanaged - //{ - // return Set(World.Entity(), target); - //} - - //public readonly EntityView Set(EcsID first, EcsID second) - //{ - // World.Set(ID, first, second); - // return this; - //} - public readonly EntityView Unset() where T : struct { World.Unset(ID); @@ -89,42 +65,6 @@ public readonly EntityView Disable() public readonly bool Has() where T : struct => World.Has(ID); - //public readonly bool Has() - // where TKind : unmanaged - // where TTarget : unmanaged - //{ - // return World.Has(ID, World.Entity(), World.Entity()); - //} - - //public readonly EntityView ChildOf(EcsID parent) - //{ - // World.Set(ID, parent); - // return this; - //} - - //public readonly void Children(Action action) - //{ - // World - // .Query() - // .With(ID) - // .Iterate( - // (ref Iterator it) => - // { - // for (int i = 0, count = it.Count; i < count; ++i) - // action(it.Entity(i)); - // } - // ); - //} - - //public readonly void ClearChildren() - //{ - // var id = World.Entity(); - // var myID = ID; // lol - // Children(v => v.Unset(id, myID)); - //} - - //public readonly EntityView Parent() => World.Entity(World.GetParent(ID)); - public readonly void Delete() => World.Delete(ID); public readonly bool Exists() => World.Exists(ID); diff --git a/src/Query.cs b/src/Query.cs index 2efbef2..15f9324 100644 --- a/src/Query.cs +++ b/src/Query.cs @@ -29,6 +29,8 @@ private Query With(int id) term.ID = id; term.Op = TermOp.With; + _terms.Span.Sort(); + return this; } @@ -43,6 +45,8 @@ private Query Without(int id) term.ID = id; term.Op = TermOp.Without; + _terms.Span.Sort(); + return this; } diff --git a/src/World.cs b/src/World.cs index e9e43f6..81c6cfd 100644 --- a/src/World.cs +++ b/src/World.cs @@ -10,6 +10,8 @@ public sealed partial class World : IDisposable internal readonly Archetype _archRoot; private readonly EntitySparseSet _entities = new(); private readonly DictionarySlim _typeIndex = new(); + private Archetype[] _archetypes = new Archetype[16]; + private int _archetypeCount; private readonly ComponentComparer _comparer; private readonly Commands _commands; private int _frame; @@ -29,18 +31,23 @@ public World() //_entities.MaxID = ECS_START_USER_ENTITY_DEFINE; } + public int EntityCount => _entities.Length; + + public ReadOnlySpan Archetypes => _archetypes.AsSpan(0, _archetypeCount); + public CommandEntityView DeferredEntity() => _commands.Entity(); public void Merge() => _commands.Merge(); - public int EntityCount => _entities.Length; - public void Dispose() { _entities.Clear(); _archRoot.Clear(); _typeIndex.Clear(); _commands.Clear(); + + Array.Clear(_archetypes, 0, _archetypeCount); + _archetypeCount = 0; } public void Optimize() @@ -229,7 +236,14 @@ bool add } ref var arch = ref GetArchetype(span, true); - arch ??= _archRoot.InsertVertex(root, span, in cmp); + if (arch == null) + { + arch = _archRoot.InsertVertex(root, span, in cmp); + + if (_archetypeCount >= _archetypes.Length) + Array.Resize(ref _archetypes, _archetypes.Length * 2); + _archetypes[_archetypeCount++] = arch; + } if (buffer != null) ArrayPool.Shared.Return(buffer); @@ -259,12 +273,14 @@ static int getHash(Span terms, bool checkSize) internal ref Archetype? GetArchetype(Span components, bool create) { var hash = getHash(components, false); - var exists = false; - ref var arch = ref Unsafe.NullRef(); if (create) { - arch = ref _typeIndex.GetOrAddValueRef(hash); + arch = ref _typeIndex.GetOrAddValueRef(hash, out var exists); + if (!exists) + { + + } } else if (_typeIndex.TryGetValue(hash, out arch)) { @@ -444,6 +460,71 @@ static void QueryRec(Archetype root, Span sortedTerms, List lis } } } + + // public IEnumerable Query() where T : ITuple + // { + // var terms = QueryLookup.Terms.AsMemory(); + // + // for (var i = 0; i < _archetypeCount; ++i) + // { + // var arch = _archetypes[i]; + // var result = arch.FindMatch(terms.Span); + // if (result == 0 && arch.Count > 0) + // { + // yield return arch; + // } + // } + // } + + public QueryInternal Query() where T : ITuple + { + var it = new QueryInternal(Archetypes); + return it; + //return new QueryIterator(Archetypes, QueryLookup.Terms); + } +} + +public readonly ref struct QueryInternal where T : ITuple +{ + private readonly ReadOnlySpan _archetypes; + public QueryInternal(ReadOnlySpan archetypes) + { + _archetypes = archetypes; + } + + public QueryIterator GetEnumerator() + { + return new QueryIterator(_archetypes, QueryLookup.Terms); + } +} + +public ref struct QueryIterator +{ + private readonly Span _terms; + private readonly ReadOnlySpan _archetypes; + private int _index; + + internal QueryIterator(ReadOnlySpan archetypes, Span terms) + { + _archetypes = archetypes; + _terms = terms; + _index = -1; + } + + public Archetype Current => _archetypes[_index]; + + public bool MoveNext() + { + while (++_index < _archetypes.Length) + { + var arch = _archetypes[_index]; + var result = arch.FindMatch(_terms); + if (result == 0 && arch.Count > 0) + return true; + } + + return false; + } } internal static class Lookup @@ -480,6 +561,7 @@ static Entity() { _arrayCreator.Add(Component.ID, count => Size > 0 ? new T[count] : Array.Empty()); _typesConvertion.Add(typeof(T), Component.ID); + _typesConvertion.Add(typeof(Not), -Component.ID); } private static int GetSize() @@ -500,6 +582,29 @@ private static int GetSize() } } +internal static class QueryLookup where T : ITuple +{ + public static readonly T Value = Activator.CreateInstance(); + public static readonly Term[] Terms; + + static QueryLookup() + { + var tuple = Value; + Terms = new Term[tuple.Length]; + + for (var i = 0; i < tuple.Length; ++i) + { + var type = tuple[i]!.GetType(); + var id = Lookup.GetID(type); + + Terms[i].ID = Math.Abs(id); + Terms[i].Op = id >= 0 ? TermOp.With : TermOp.Without; + } + + Array.Sort(Terms); + } +} + struct EcsRecord { public Archetype Archetype;