diff --git a/src/Archetype.cs b/src/Archetype.cs index 45bd718..791ba96 100644 --- a/src/Archetype.cs +++ b/src/Archetype.cs @@ -9,7 +9,10 @@ internal class Archetype public ArchetypeSignature Signature; public NativeArray Entities = new NativeArray(); - public SortedDictionary Edges = new SortedDictionary(); + public SortedDictionary AddEdges = + new SortedDictionary(); + public SortedDictionary RemoveEdges = + new SortedDictionary(); public int Count => Entities.Count; @@ -25,13 +28,6 @@ internal class Archetype return Entities.Count - 1; } - public int Transfer(int row, Archetype transferTo) - { - var newIndex = transferTo.Append(Entities[row]); - Entities.Delete(row); - return newIndex; - } - public void ClearAll() { for (int i = Entities.Count - 1; i >= 0; i -= 1) diff --git a/src/ArchetypeSignature.cs b/src/ArchetypeSignature.cs index cabaeb7..5dac880 100644 --- a/src/ArchetypeSignature.cs +++ b/src/ArchetypeSignature.cs @@ -41,7 +41,7 @@ internal class ArchetypeSignature : IEquatable public void CopyTo(ArchetypeSignature other) { - foreach (var id in other.Ids.AsSpan()) + foreach (var id in Ids.AsSpan()) { other.Ids.Add(id); } diff --git a/src/Collections/NativeArray.cs b/src/Collections/NativeArray.cs index 9661de8..10e48ca 100644 --- a/src/Collections/NativeArray.cs +++ b/src/Collections/NativeArray.cs @@ -69,7 +69,7 @@ public unsafe class NativeArray : IDisposable where T : unmanaged // Fills gap by copying final element to the deleted index public void Delete(int index) { - if (Count > 1) + if (index != Count - 1) { NativeMemory.Copy( (void*) (Elements + ((Count - 1) * ElementSize)), diff --git a/src/Collections/NativeArrayUntyped.cs b/src/Collections/NativeArrayUntyped.cs index e66336d..020c5a0 100644 --- a/src/Collections/NativeArrayUntyped.cs +++ b/src/Collections/NativeArrayUntyped.cs @@ -53,7 +53,7 @@ internal unsafe class NativeArray : IDisposable // Fills gap by copying final element to the deleted index public void Delete(int index) { - if (Count > 1) + if (index != Count - 1) { NativeMemory.Copy( (void*) (Elements + ((Count - 1) * ElementSize)), diff --git a/src/ComponentStorage.cs b/src/ComponentStorage.cs index 7368102..4cfb619 100644 --- a/src/ComponentStorage.cs +++ b/src/ComponentStorage.cs @@ -30,7 +30,6 @@ namespace MoonTools.ECS public ref T Get(in Entity entity) where T : unmanaged { - return ref Components.Get(EntityIDToStorageIndex[entity]); } diff --git a/src/Filter.cs b/src/Filter.cs index 9b54c75..3d51524 100644 --- a/src/Filter.cs +++ b/src/Filter.cs @@ -59,17 +59,14 @@ public class Filter // WARNING: this WILL crash if the index is out of range! public Entity NthEntity(int index) { - var count = 0; - foreach (var archetype in Archetypes) { - count += archetype.Count; - if (index < count) + if (index < archetype.Count) { return archetype.Entities[index]; } - index -= count; + index -= archetype.Count; } throw new InvalidOperationException("Filter index out of range!"); @@ -135,12 +132,12 @@ public class Filter // breadth-first search // ignore excluded component edges - foreach (var (componentId, edge) in current.Edges) + foreach (var (componentId, edge) in current.AddEdges) { - if (!Explored.Contains(edge.Add) && !filter.Excluded.Contains(componentId)) + if (!filter.Excluded.Contains(componentId)) { - Explored.Add(edge.Add); - ArchetypeSearchQueue.Enqueue(edge.Add); + Explored.Add(edge); + ArchetypeSearchQueue.Enqueue(edge); } } } diff --git a/src/TypeId.cs b/src/TypeId.cs index f94a2a7..62bc5d6 100644 --- a/src/TypeId.cs +++ b/src/TypeId.cs @@ -2,4 +2,10 @@ using System; namespace MoonTools.ECS; -public readonly record struct TypeId(uint Value); +public readonly record struct TypeId(uint Value) : IComparable +{ + public int CompareTo(TypeId other) + { + return Value.CompareTo(other.Value); + } +} diff --git a/src/World.cs b/src/World.cs index 6b69d80..b82ee8f 100644 --- a/src/World.cs +++ b/src/World.cs @@ -90,6 +90,14 @@ namespace MoonTools.ECS var entity = new Entity(EntityIdAssigner.Assign()); EntityIndex.Add(entity, new ArchetypeRecord(EmptyArchetype, EmptyArchetype.Count)); EmptyArchetype.Append(entity); + + if (!EntityRelationIndex.ContainsKey(entity)) + { + EntityRelationIndex.Add(entity, new IndexableSet()); + } + + EntityTags[entity] = tag; + return entity; } @@ -177,7 +185,7 @@ namespace MoonTools.ECS if (!componentStorage.Set(entity, component)) { - UpdateArchetype(entity, true); + TransferArchetype(entity, FindArchetypeByAdd(entity)); } } @@ -187,48 +195,79 @@ namespace MoonTools.ECS if (componentStorage.Remove(entity)) { - UpdateArchetype(entity, false); + TransferArchetype(entity, FindArchetypeByRemove(entity)); } } - private void UpdateArchetype(in Entity entity, bool insert) + private Archetype FindArchetypeByAdd(in Entity entity) { - Archetype? nextArchetype; - var componentTypeId = TypeToId[typeof(T)]; var record = EntityIndex[entity]; var archetype = record.Archetype; - if (archetype.Edges.TryGetValue(TypeToId[typeof(T)], out var edge)) + if (archetype.AddEdges.TryGetValue(componentTypeId, out var nextArchetype)) { - nextArchetype = insert ? edge.Add : edge.Remove; - } - else - { - var nextSignature = new ArchetypeSignature(archetype.Signature.Count + 1); - archetype.Signature.CopyTo(nextSignature); - - if (insert) - { - nextSignature.Insert(componentTypeId); - } - else - { - nextSignature.Remove(componentTypeId); - } - - if (!ArchetypeIndex.TryGetValue(nextSignature, out nextArchetype)) - { - nextArchetype = CreateArchetype(nextSignature); - } - - var newEdge = new ArchetypeEdge(nextArchetype, archetype); - archetype.Edges.Add(componentTypeId, newEdge); - nextArchetype.Edges.Add(componentTypeId, newEdge); + return nextArchetype; } - var newRow = archetype.Transfer(record.Row, nextArchetype); - EntityIndex[entity] = new ArchetypeRecord(nextArchetype, newRow); + var nextSignature = new ArchetypeSignature(archetype.Signature.Count + 1); + archetype.Signature.CopyTo(nextSignature); + nextSignature.Insert(componentTypeId); + + if (!ArchetypeIndex.TryGetValue(nextSignature, out nextArchetype)) + { + nextArchetype = CreateArchetype(nextSignature); + } + + archetype.AddEdges.Add(componentTypeId, nextArchetype); + nextArchetype.RemoveEdges.Add(componentTypeId, archetype); + + return nextArchetype; + } + + private Archetype FindArchetypeByRemove(in Entity entity) + { + var componentTypeId = TypeToId[typeof(T)]; + var record = EntityIndex[entity]; + var archetype = record.Archetype; + + if (archetype.RemoveEdges.TryGetValue(componentTypeId, out var nextArchetype)) + { + return nextArchetype; + } + + var nextSignature = new ArchetypeSignature(archetype.Signature.Count + 1); + archetype.Signature.CopyTo(nextSignature); + nextSignature.Remove(componentTypeId); + + if (!ArchetypeIndex.TryGetValue(nextSignature, out nextArchetype)) + { + nextArchetype = CreateArchetype(nextSignature); + } + + archetype.RemoveEdges.Add(componentTypeId, nextArchetype); + nextArchetype.AddEdges.Add(componentTypeId, archetype); + + return nextArchetype; + } + + private void TransferArchetype(in Entity entity, Archetype nextArchetype) + { + var record = EntityIndex[entity]; + var archetype = record.Archetype; + var row = record.Row; + + // fill the gap + if (row != archetype.Count - 1) + { + var lastEntity = archetype.Entities[archetype.Count - 1]; + archetype.Entities[row] = lastEntity; + EntityIndex[lastEntity] = new ArchetypeRecord(archetype, row); + } + + archetype.Entities.RemoveLastElement(); + nextArchetype.Entities.Append(entity); + EntityIndex[entity] = new ArchetypeRecord(nextArchetype, nextArchetype.Count - 1); } // RELATIONS