misc fixes

pull/6/head
cosmonaut 2023-11-03 15:39:30 -07:00
parent 001b6714cc
commit 8774314f62
8 changed files with 91 additions and 54 deletions

View File

@ -9,7 +9,10 @@ internal class Archetype
public ArchetypeSignature Signature; public ArchetypeSignature Signature;
public NativeArray<Entity> Entities = new NativeArray<Entity>(); public NativeArray<Entity> Entities = new NativeArray<Entity>();
public SortedDictionary<TypeId, ArchetypeEdge> Edges = new SortedDictionary<TypeId, ArchetypeEdge>(); public SortedDictionary<TypeId, Archetype> AddEdges =
new SortedDictionary<TypeId, Archetype>();
public SortedDictionary<TypeId, Archetype> RemoveEdges =
new SortedDictionary<TypeId, Archetype>();
public int Count => Entities.Count; public int Count => Entities.Count;
@ -25,13 +28,6 @@ internal class Archetype
return Entities.Count - 1; 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() public void ClearAll()
{ {
for (int i = Entities.Count - 1; i >= 0; i -= 1) for (int i = Entities.Count - 1; i >= 0; i -= 1)

View File

@ -41,7 +41,7 @@ internal class ArchetypeSignature : IEquatable<ArchetypeSignature>
public void CopyTo(ArchetypeSignature other) public void CopyTo(ArchetypeSignature other)
{ {
foreach (var id in other.Ids.AsSpan()) foreach (var id in Ids.AsSpan())
{ {
other.Ids.Add(id); other.Ids.Add(id);
} }

View File

@ -69,7 +69,7 @@ public unsafe class NativeArray<T> : IDisposable where T : unmanaged
// Fills gap by copying final element to the deleted index // Fills gap by copying final element to the deleted index
public void Delete(int index) public void Delete(int index)
{ {
if (Count > 1) if (index != Count - 1)
{ {
NativeMemory.Copy( NativeMemory.Copy(
(void*) (Elements + ((Count - 1) * ElementSize)), (void*) (Elements + ((Count - 1) * ElementSize)),

View File

@ -53,7 +53,7 @@ internal unsafe class NativeArray : IDisposable
// Fills gap by copying final element to the deleted index // Fills gap by copying final element to the deleted index
public void Delete(int index) public void Delete(int index)
{ {
if (Count > 1) if (index != Count - 1)
{ {
NativeMemory.Copy( NativeMemory.Copy(
(void*) (Elements + ((Count - 1) * ElementSize)), (void*) (Elements + ((Count - 1) * ElementSize)),

View File

@ -30,7 +30,6 @@ namespace MoonTools.ECS
public ref T Get<T>(in Entity entity) where T : unmanaged public ref T Get<T>(in Entity entity) where T : unmanaged
{ {
return ref Components.Get<T>(EntityIDToStorageIndex[entity]); return ref Components.Get<T>(EntityIDToStorageIndex[entity]);
} }

View File

@ -59,17 +59,14 @@ public class Filter
// 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) public Entity NthEntity(int index)
{ {
var count = 0;
foreach (var archetype in Archetypes) foreach (var archetype in Archetypes)
{ {
count += archetype.Count; if (index < archetype.Count)
if (index < count)
{ {
return archetype.Entities[index]; return archetype.Entities[index];
} }
index -= count; index -= archetype.Count;
} }
throw new InvalidOperationException("Filter index out of range!"); throw new InvalidOperationException("Filter index out of range!");
@ -135,12 +132,12 @@ public class Filter
// breadth-first search // breadth-first search
// ignore excluded component edges // 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); Explored.Add(edge);
ArchetypeSearchQueue.Enqueue(edge.Add); ArchetypeSearchQueue.Enqueue(edge);
} }
} }
} }

View File

@ -2,4 +2,10 @@ using System;
namespace MoonTools.ECS; namespace MoonTools.ECS;
public readonly record struct TypeId(uint Value); public readonly record struct TypeId(uint Value) : IComparable<TypeId>
{
public int CompareTo(TypeId other)
{
return Value.CompareTo(other.Value);
}
}

View File

@ -90,6 +90,14 @@ namespace MoonTools.ECS
var entity = new Entity(EntityIdAssigner.Assign()); var entity = new Entity(EntityIdAssigner.Assign());
EntityIndex.Add(entity, new ArchetypeRecord(EmptyArchetype, EmptyArchetype.Count)); EntityIndex.Add(entity, new ArchetypeRecord(EmptyArchetype, EmptyArchetype.Count));
EmptyArchetype.Append(entity); EmptyArchetype.Append(entity);
if (!EntityRelationIndex.ContainsKey(entity))
{
EntityRelationIndex.Add(entity, new IndexableSet<TypeId>());
}
EntityTags[entity] = tag;
return entity; return entity;
} }
@ -177,7 +185,7 @@ namespace MoonTools.ECS
if (!componentStorage.Set(entity, component)) if (!componentStorage.Set(entity, component))
{ {
UpdateArchetype<T>(entity, true); TransferArchetype(entity, FindArchetypeByAdd<T>(entity));
} }
} }
@ -187,48 +195,79 @@ namespace MoonTools.ECS
if (componentStorage.Remove(entity)) if (componentStorage.Remove(entity))
{ {
UpdateArchetype<T>(entity, false); TransferArchetype(entity, FindArchetypeByRemove<T>(entity));
} }
} }
private void UpdateArchetype<T>(in Entity entity, bool insert) private Archetype FindArchetypeByAdd<T>(in Entity entity)
{ {
Archetype? nextArchetype;
var componentTypeId = TypeToId[typeof(T)]; var componentTypeId = TypeToId[typeof(T)];
var record = EntityIndex[entity]; var record = EntityIndex[entity];
var archetype = record.Archetype; 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; return nextArchetype;
}
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);
} }
var newRow = archetype.Transfer(record.Row, nextArchetype); var nextSignature = new ArchetypeSignature(archetype.Signature.Count + 1);
EntityIndex[entity] = new ArchetypeRecord(nextArchetype, newRow); 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<T>(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 // RELATIONS