hello filters my old friend
parent
e6059a2f0a
commit
c6f8b65b32
|
@ -1,38 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using MoonTools.ECS.Collections;
|
|
||||||
|
|
||||||
namespace MoonTools.ECS;
|
|
||||||
|
|
||||||
internal class Archetype
|
|
||||||
{
|
|
||||||
public World World;
|
|
||||||
public ArchetypeSignature Signature;
|
|
||||||
public NativeArray<Entity> Entities = new NativeArray<Entity>();
|
|
||||||
|
|
||||||
public SortedDictionary<TypeId, Archetype> AddEdges =
|
|
||||||
new SortedDictionary<TypeId, Archetype>();
|
|
||||||
public SortedDictionary<TypeId, Archetype> RemoveEdges =
|
|
||||||
new SortedDictionary<TypeId, Archetype>();
|
|
||||||
|
|
||||||
public int Count => Entities.Count;
|
|
||||||
|
|
||||||
public Archetype(World world, ArchetypeSignature signature)
|
|
||||||
{
|
|
||||||
World = world;
|
|
||||||
Signature = signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Append(Entity entity)
|
|
||||||
{
|
|
||||||
Entities.Append(entity);
|
|
||||||
return Entities.Count - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearAll()
|
|
||||||
{
|
|
||||||
for (int i = Entities.Count - 1; i >= 0; i -= 1)
|
|
||||||
{
|
|
||||||
World.Destroy(Entities[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
namespace MoonTools.ECS;
|
|
||||||
|
|
||||||
internal readonly record struct ArchetypeEdge(Archetype Add, Archetype Remove);
|
|
|
@ -1,3 +0,0 @@
|
||||||
namespace MoonTools.ECS;
|
|
||||||
|
|
||||||
internal readonly record struct ArchetypeRecord(Archetype Archetype, int Row);
|
|
|
@ -1,89 +0,0 @@
|
||||||
using System;
|
|
||||||
using MoonTools.ECS.Collections;
|
|
||||||
|
|
||||||
namespace MoonTools.ECS;
|
|
||||||
|
|
||||||
internal class ArchetypeSignature : IEquatable<ArchetypeSignature>
|
|
||||||
{
|
|
||||||
public static ArchetypeSignature Empty = new ArchetypeSignature(0);
|
|
||||||
|
|
||||||
IndexableSet<TypeId> Ids;
|
|
||||||
|
|
||||||
public int Count => Ids.Count;
|
|
||||||
|
|
||||||
public TypeId this[int i] => Ids[i];
|
|
||||||
|
|
||||||
public ArchetypeSignature()
|
|
||||||
{
|
|
||||||
Ids = new IndexableSet<TypeId>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArchetypeSignature(int capacity)
|
|
||||||
{
|
|
||||||
Ids = new IndexableSet<TypeId>(capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maintains sorted order
|
|
||||||
public void Insert(TypeId componentId)
|
|
||||||
{
|
|
||||||
Ids.Add(componentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Remove(TypeId componentId)
|
|
||||||
{
|
|
||||||
Ids.Remove(componentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Contains(TypeId componentId)
|
|
||||||
{
|
|
||||||
return Ids.Contains(componentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CopyTo(ArchetypeSignature other)
|
|
||||||
{
|
|
||||||
foreach (var id in Ids.AsSpan())
|
|
||||||
{
|
|
||||||
other.Ids.Add(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
|
||||||
{
|
|
||||||
return obj is ArchetypeSignature signature && Equals(signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(ArchetypeSignature? other)
|
|
||||||
{
|
|
||||||
if (other == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Ids.Count != other.Ids.Count)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < Ids.Count; i += 1)
|
|
||||||
{
|
|
||||||
if (Ids[i] != other.Ids[i])
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
var hashcode = 1;
|
|
||||||
|
|
||||||
foreach (var id in Ids)
|
|
||||||
{
|
|
||||||
hashcode = HashCode.Combine(hashcode, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashcode;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -76,22 +76,6 @@ internal unsafe class NativeArray : IDisposable
|
||||||
Count += 1;
|
Count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CopyElementToEnd(int index, NativeArray other)
|
|
||||||
{
|
|
||||||
if (other.Count >= other.Capacity)
|
|
||||||
{
|
|
||||||
other.Resize();
|
|
||||||
}
|
|
||||||
|
|
||||||
NativeMemory.Copy(
|
|
||||||
(void*) (Elements + (index * ElementSize)),
|
|
||||||
(void*) (other.Elements + (other.Count * ElementSize)),
|
|
||||||
(nuint) ElementSize
|
|
||||||
);
|
|
||||||
|
|
||||||
other.Count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CopyAllTo(NativeArray other)
|
public void CopyAllTo(NativeArray other)
|
||||||
{
|
{
|
||||||
if (Count >= other.Capacity)
|
if (Count >= other.Capacity)
|
||||||
|
|
|
@ -9,13 +9,15 @@ namespace MoonTools.ECS
|
||||||
internal readonly Dictionary<Entity, int> EntityIDToStorageIndex = new Dictionary<Entity, int>(16);
|
internal readonly Dictionary<Entity, int> EntityIDToStorageIndex = new Dictionary<Entity, int>(16);
|
||||||
internal readonly NativeArray Components;
|
internal readonly NativeArray Components;
|
||||||
internal readonly NativeArray<Entity> EntityIDs;
|
internal readonly NativeArray<Entity> EntityIDs;
|
||||||
|
internal readonly TypeId TypeId;
|
||||||
|
|
||||||
private bool disposed;
|
private bool disposed;
|
||||||
|
|
||||||
public ComponentStorage(int elementSize)
|
public ComponentStorage(TypeId typeId, int elementSize)
|
||||||
{
|
{
|
||||||
Components = new NativeArray(elementSize);
|
Components = new NativeArray(elementSize);
|
||||||
EntityIDs = new NativeArray<Entity>();
|
EntityIDs = new NativeArray<Entity>();
|
||||||
|
TypeId = typeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Any()
|
public bool Any()
|
||||||
|
|
200
src/Filter.cs
200
src/Filter.cs
|
@ -1,193 +1,77 @@
|
||||||
using System;
|
using MoonTools.ECS.Collections;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace MoonTools.ECS;
|
namespace MoonTools.ECS;
|
||||||
|
|
||||||
// TODO: do we want to get fancy with queries beyond Include and Exclude?
|
// TODO: do we want to get fancy with queries beyond Include and Exclude?
|
||||||
public class Filter
|
public class Filter
|
||||||
{
|
{
|
||||||
private Archetype EmptyArchetype;
|
private World World;
|
||||||
private HashSet<TypeId> Included;
|
internal FilterSignature Signature;
|
||||||
private HashSet<TypeId> Excluded;
|
|
||||||
|
|
||||||
public EntityEnumerator Entities => new EntityEnumerator(this);
|
internal IndexableSet<Entity> EntitySet = new IndexableSet<Entity>();
|
||||||
internal ArchetypeEnumerator Archetypes => new ArchetypeEnumerator(this);
|
|
||||||
public RandomEntityEnumerator EntitiesInRandomOrder => new RandomEntityEnumerator(this);
|
|
||||||
|
|
||||||
public bool Empty
|
public ReverseSpanEnumerator<Entity> Entities => EntitySet.GetEnumerator();
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var empty = true;
|
|
||||||
|
|
||||||
foreach (var archetype in Archetypes)
|
public bool Empty => EntitySet.Count == 0;
|
||||||
{
|
public int Count => EntitySet.Count;
|
||||||
if (archetype.Count > 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var count = 0;
|
|
||||||
|
|
||||||
foreach (var archetype in Archetypes)
|
|
||||||
{
|
|
||||||
count += archetype.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Entity RandomEntity
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var randomIndex = RandomManager.Next(Count);
|
|
||||||
return NthEntity(randomIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) => EntitySet[index];
|
||||||
|
|
||||||
|
// WARNING: this WILL crash if the filter is empty!
|
||||||
|
public Entity RandomEntity => EntitySet[RandomManager.Next(EntitySet.Count)];
|
||||||
|
public RandomEntityEnumerator EntitiesInRandomOrder => new RandomEntityEnumerator(this);
|
||||||
|
|
||||||
|
internal Filter(World world, FilterSignature signature)
|
||||||
{
|
{
|
||||||
foreach (var archetype in Archetypes)
|
World = world;
|
||||||
{
|
Signature = signature;
|
||||||
if (index < archetype.Count)
|
|
||||||
{
|
|
||||||
return archetype.Entities[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
index -= archetype.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidOperationException("Filter index out of range!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DestroyAllEntities()
|
public void DestroyAllEntities()
|
||||||
{
|
{
|
||||||
foreach (var archetype in Archetypes)
|
foreach (var entity in EntitySet)
|
||||||
{
|
{
|
||||||
archetype.ClearAll();
|
World.Destroy(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Filter(Archetype emptyArchetype, HashSet<TypeId> included, HashSet<TypeId> excluded)
|
internal void Check(Entity entity)
|
||||||
{
|
{
|
||||||
EmptyArchetype = emptyArchetype;
|
foreach (var type in Signature.Included)
|
||||||
Included = included;
|
|
||||||
Excluded = excluded;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal ref struct ArchetypeEnumerator
|
|
||||||
{
|
|
||||||
private Archetype CurrentArchetype;
|
|
||||||
|
|
||||||
// TODO: pool these
|
|
||||||
private Queue<Archetype> ArchetypeQueue = new Queue<Archetype>();
|
|
||||||
private Queue<Archetype> ArchetypeSearchQueue = new Queue<Archetype>();
|
|
||||||
private HashSet<Archetype> Explored = new HashSet<Archetype>();
|
|
||||||
|
|
||||||
public ArchetypeEnumerator GetEnumerator() => this;
|
|
||||||
|
|
||||||
public ArchetypeEnumerator(Filter filter)
|
|
||||||
{
|
{
|
||||||
var empty = filter.EmptyArchetype;
|
if (!World.Has(entity, type))
|
||||||
ArchetypeSearchQueue.Enqueue(empty);
|
|
||||||
|
|
||||||
// TODO: can we cache this search effectively?
|
|
||||||
while (ArchetypeSearchQueue.TryDequeue(out var current))
|
|
||||||
{
|
{
|
||||||
// exclude the empty archetype
|
EntitySet.Remove(entity);
|
||||||
var satisfiesFilter = filter.Included.Count != 0;
|
return;
|
||||||
|
|
||||||
foreach (var componentId in filter.Included)
|
|
||||||
{
|
|
||||||
if (!current.Signature.Contains(componentId))
|
|
||||||
{
|
|
||||||
satisfiesFilter = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var componentId in filter.Excluded)
|
|
||||||
{
|
|
||||||
if (current.Signature.Contains(componentId))
|
|
||||||
{
|
|
||||||
satisfiesFilter = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (satisfiesFilter)
|
|
||||||
{
|
|
||||||
ArchetypeQueue.Enqueue(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
// breadth-first search
|
|
||||||
// ignore excluded component edges
|
|
||||||
foreach (var (componentId, archetype) in current.AddEdges)
|
|
||||||
{
|
|
||||||
if (!Explored.Contains(archetype) && !filter.Excluded.Contains(componentId))
|
|
||||||
{
|
|
||||||
Explored.Add(archetype);
|
|
||||||
ArchetypeSearchQueue.Enqueue(archetype);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var (componentId, archetype) in current.RemoveEdges)
|
|
||||||
{
|
|
||||||
if (!Explored.Contains(archetype))
|
|
||||||
{
|
|
||||||
Explored.Add(archetype);
|
|
||||||
ArchetypeSearchQueue.Enqueue(archetype);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool MoveNext()
|
foreach (var type in Signature.Excluded)
|
||||||
{
|
{
|
||||||
return ArchetypeQueue.TryDequeue(out CurrentArchetype!);
|
if (World.Has(entity, type))
|
||||||
}
|
|
||||||
|
|
||||||
public Archetype Current => CurrentArchetype;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref struct EntityEnumerator
|
|
||||||
{
|
|
||||||
private Entity CurrentEntity;
|
|
||||||
|
|
||||||
public EntityEnumerator GetEnumerator() => this;
|
|
||||||
|
|
||||||
// TODO: pool this
|
|
||||||
Queue<Entity> EntityQueue = new Queue<Entity>();
|
|
||||||
|
|
||||||
internal EntityEnumerator(Filter filter)
|
|
||||||
{
|
|
||||||
var archetypeEnumerator = new ArchetypeEnumerator(filter);
|
|
||||||
|
|
||||||
foreach (var archetype in archetypeEnumerator)
|
|
||||||
{
|
{
|
||||||
foreach (var entity in archetype.Entities)
|
EntitySet.Remove(entity);
|
||||||
{
|
return;
|
||||||
EntityQueue.Enqueue(entity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool MoveNext()
|
EntitySet.Add(entity);
|
||||||
{
|
}
|
||||||
return EntityQueue.TryDequeue(out CurrentEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Entity Current => CurrentEntity;
|
internal void AddEntity(in Entity entity)
|
||||||
|
{
|
||||||
|
EntitySet.Add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void RemoveEntity(in Entity entity)
|
||||||
|
{
|
||||||
|
EntitySet.Remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Clear()
|
||||||
|
{
|
||||||
|
EntitySet.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref struct RandomEntityEnumerator
|
public ref struct RandomEntityEnumerator
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using MoonTools.ECS.Collections;
|
||||||
|
|
||||||
namespace MoonTools.ECS
|
namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
public struct FilterBuilder
|
public struct FilterBuilder
|
||||||
{
|
{
|
||||||
World World;
|
World World;
|
||||||
HashSet<TypeId> Included;
|
IndexableSet<TypeId> Included;
|
||||||
HashSet<TypeId> Excluded;
|
IndexableSet<TypeId> Excluded;
|
||||||
|
|
||||||
internal FilterBuilder(World world)
|
internal FilterBuilder(World world)
|
||||||
{
|
{
|
||||||
World = world;
|
World = world;
|
||||||
Included = new HashSet<TypeId>();
|
Included = new IndexableSet<TypeId>();
|
||||||
Excluded = new HashSet<TypeId>();
|
Excluded = new IndexableSet<TypeId>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private FilterBuilder(World world, HashSet<TypeId> included, HashSet<TypeId> excluded)
|
private FilterBuilder(World world, IndexableSet<TypeId> included, IndexableSet<TypeId> excluded)
|
||||||
{
|
{
|
||||||
World = world;
|
World = world;
|
||||||
Included = included;
|
Included = included;
|
||||||
|
@ -24,19 +25,20 @@ namespace MoonTools.ECS
|
||||||
|
|
||||||
public FilterBuilder Include<T>() where T : unmanaged
|
public FilterBuilder Include<T>() where T : unmanaged
|
||||||
{
|
{
|
||||||
Included.Add(World.GetTypeId<T>());
|
Included.Add(World.GetComponentTypeId<T>());
|
||||||
return new FilterBuilder(World, Included, Excluded);
|
return new FilterBuilder(World, Included, Excluded);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FilterBuilder Exclude<T>() where T : unmanaged
|
public FilterBuilder Exclude<T>() where T : unmanaged
|
||||||
{
|
{
|
||||||
Excluded.Add(World.GetTypeId<T>());
|
Excluded.Add(World.GetComponentTypeId<T>());
|
||||||
return new FilterBuilder(World, Included, Excluded);
|
return new FilterBuilder(World, Included, Excluded);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Filter Build()
|
public Filter Build()
|
||||||
{
|
{
|
||||||
return new Filter(World.EmptyArchetype, Included, Excluded);
|
var signature = new FilterSignature(Included, Excluded);
|
||||||
|
return World.GetFilter(signature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using MoonTools.ECS.Collections;
|
using MoonTools.ECS.Collections;
|
||||||
|
|
||||||
namespace MoonTools.ECS
|
namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
public struct FilterSignature : IEquatable<FilterSignature>
|
public struct FilterSignature : IEquatable<FilterSignature>
|
||||||
{
|
{
|
||||||
public readonly IndexableSet<int> Included;
|
public readonly IndexableSet<TypeId> Included;
|
||||||
public readonly IndexableSet<int> Excluded;
|
public readonly IndexableSet<TypeId> Excluded;
|
||||||
|
|
||||||
public FilterSignature(IndexableSet<int> included, IndexableSet<int> excluded)
|
public FilterSignature(IndexableSet<TypeId> included, IndexableSet<TypeId> excluded)
|
||||||
{
|
{
|
||||||
Included = included;
|
Included = included;
|
||||||
Excluded = excluded;
|
Excluded = excluded;
|
||||||
|
|
|
@ -7,14 +7,17 @@ internal class IdAssigner
|
||||||
uint Next;
|
uint Next;
|
||||||
NativeArray<uint> AvailableIds = new NativeArray<uint>();
|
NativeArray<uint> AvailableIds = new NativeArray<uint>();
|
||||||
|
|
||||||
public uint Assign()
|
public uint Assign(out bool recycled)
|
||||||
{
|
{
|
||||||
if (!AvailableIds.TryPop(out var id))
|
recycled = AvailableIds.TryPop(out var id);
|
||||||
|
|
||||||
|
if (recycled)
|
||||||
{
|
{
|
||||||
id = Next;
|
return id;
|
||||||
Next += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id = Next;
|
||||||
|
Next += 1;
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
130
src/Snapshot.cs
130
src/Snapshot.cs
|
@ -9,51 +9,38 @@ public class Snapshot
|
||||||
{
|
{
|
||||||
private Dictionary<TypeId, ComponentSnapshot> ComponentSnapshots = new Dictionary<TypeId, ComponentSnapshot>();
|
private Dictionary<TypeId, ComponentSnapshot> ComponentSnapshots = new Dictionary<TypeId, ComponentSnapshot>();
|
||||||
|
|
||||||
private Dictionary<ArchetypeSignature, ArchetypeSnapshot> ArchetypeSnapshots =
|
private Dictionary<FilterSignature, List<Entity>> Filters = new Dictionary<FilterSignature, List<Entity>>();
|
||||||
new Dictionary<ArchetypeSignature, ArchetypeSnapshot>();
|
|
||||||
|
|
||||||
private Dictionary<TypeId, RelationSnapshot> RelationSnapshots =
|
private Dictionary<TypeId, RelationSnapshot> RelationSnapshots =
|
||||||
new Dictionary<TypeId, RelationSnapshot>();
|
new Dictionary<TypeId, RelationSnapshot>();
|
||||||
|
|
||||||
|
|
||||||
private Dictionary<Entity, ArchetypeRecord> EntityIndex = new Dictionary<Entity, ArchetypeRecord>();
|
|
||||||
|
|
||||||
private Dictionary<Entity, IndexableSet<TypeId>> EntityRelationIndex =
|
private Dictionary<Entity, IndexableSet<TypeId>> EntityRelationIndex =
|
||||||
new Dictionary<Entity, IndexableSet<TypeId>>();
|
new Dictionary<Entity, IndexableSet<TypeId>>();
|
||||||
|
|
||||||
|
private Dictionary<Entity, IndexableSet<TypeId>> EntityComponentIndex =
|
||||||
|
new Dictionary<Entity, IndexableSet<TypeId>>();
|
||||||
|
|
||||||
private Dictionary<Entity, string> EntityTags = new Dictionary<Entity, string>();
|
private Dictionary<Entity, string> EntityTags = new Dictionary<Entity, string>();
|
||||||
|
|
||||||
private IdAssigner EntityIdAssigner = new IdAssigner();
|
private IdAssigner EntityIdAssigner = new IdAssigner();
|
||||||
|
|
||||||
public int Count
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var count = 0;
|
|
||||||
|
|
||||||
foreach (var snapshot in ArchetypeSnapshots.Values)
|
|
||||||
{
|
|
||||||
count += snapshot.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Restore(World world)
|
public void Restore(World world)
|
||||||
{
|
{
|
||||||
// restore archetype storage
|
// restore id assigner state
|
||||||
foreach (var (archetypeSignature, archetypeSnapshot) in ArchetypeSnapshots)
|
EntityIdAssigner.CopyTo(world.EntityIdAssigner);
|
||||||
{
|
|
||||||
var archetype = world.ArchetypeIndex[archetypeSignature];
|
|
||||||
archetypeSnapshot.Restore(archetype);
|
|
||||||
}
|
|
||||||
|
|
||||||
// restore entity index
|
// restore filter states
|
||||||
world.EntityIndex.Clear();
|
// this could be sped up if we figured out a direct IndexableSet copy
|
||||||
foreach (var (id, ArchetypeRecord) in EntityIndex)
|
foreach (var (signature, entityList) in Filters)
|
||||||
{
|
{
|
||||||
world.EntityIndex[id] = ArchetypeRecord;
|
var filter = world.FilterIndex[signature];
|
||||||
|
|
||||||
|
filter.Clear();
|
||||||
|
|
||||||
|
foreach (var entity in entityList)
|
||||||
|
{
|
||||||
|
filter.AddEntity(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore components
|
// restore components
|
||||||
|
@ -63,9 +50,6 @@ public class Snapshot
|
||||||
componentSnapshot.Restore(componentStorage);
|
componentSnapshot.Restore(componentStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore id assigner state
|
|
||||||
EntityIdAssigner.CopyTo(world.EntityIdAssigner);
|
|
||||||
|
|
||||||
// restore relation state
|
// restore relation state
|
||||||
foreach (var (typeId, relationSnapshot) in RelationSnapshots)
|
foreach (var (typeId, relationSnapshot) in RelationSnapshots)
|
||||||
{
|
{
|
||||||
|
@ -85,6 +69,19 @@ public class Snapshot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// restore entity component index state
|
||||||
|
// FIXME: arrghghhh this is so slow
|
||||||
|
foreach (var (id, componentTypeSet) in EntityComponentIndex)
|
||||||
|
{
|
||||||
|
world.EntityComponentIndex[id].Clear();
|
||||||
|
|
||||||
|
foreach (var typeId in componentTypeSet)
|
||||||
|
{
|
||||||
|
world.EntityComponentIndex[id].Add(typeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore entity tags
|
||||||
foreach (var (id, s) in EntityTags)
|
foreach (var (id, s) in EntityTags)
|
||||||
{
|
{
|
||||||
world.EntityTags[id] = s;
|
world.EntityTags[id] = s;
|
||||||
|
@ -96,17 +93,10 @@ public class Snapshot
|
||||||
// copy id assigner state
|
// copy id assigner state
|
||||||
world.EntityIdAssigner.CopyTo(EntityIdAssigner);
|
world.EntityIdAssigner.CopyTo(EntityIdAssigner);
|
||||||
|
|
||||||
// copy entity index
|
// copy filter states
|
||||||
EntityIndex.Clear();
|
foreach (var (_, filter) in world.FilterIndex)
|
||||||
foreach (var (id, ArchetypeRecord) in world.EntityIndex)
|
|
||||||
{
|
{
|
||||||
EntityIndex[id] = ArchetypeRecord;
|
TakeFilterSnapshot(filter);
|
||||||
}
|
|
||||||
|
|
||||||
// copy archetypes
|
|
||||||
foreach (var archetype in world.ArchetypeIndex.Values)
|
|
||||||
{
|
|
||||||
TakeArchetypeSnapshot(archetype);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy components
|
// copy components
|
||||||
|
@ -138,21 +128,44 @@ public class Snapshot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copy entity component index
|
||||||
|
// FIXME: arghhhh this is so slow
|
||||||
|
foreach (var (id, componentTypeSet) in world.EntityComponentIndex)
|
||||||
|
{
|
||||||
|
if (!EntityComponentIndex.ContainsKey(id))
|
||||||
|
{
|
||||||
|
EntityComponentIndex.Add(id, new IndexableSet<TypeId>());
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityComponentIndex[id].Clear();
|
||||||
|
|
||||||
|
foreach (var typeId in componentTypeSet)
|
||||||
|
{
|
||||||
|
EntityComponentIndex[id].Add(typeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy entity tags
|
||||||
foreach (var (id, s) in world.EntityTags)
|
foreach (var (id, s) in world.EntityTags)
|
||||||
{
|
{
|
||||||
EntityTags[id] = s;
|
EntityTags[id] = s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TakeArchetypeSnapshot(Archetype archetype)
|
private void TakeFilterSnapshot(Filter filter)
|
||||||
{
|
{
|
||||||
if (!ArchetypeSnapshots.TryGetValue(archetype.Signature, out var archetypeSnapshot))
|
if (!Filters.TryGetValue(filter.Signature, out var entities))
|
||||||
{
|
{
|
||||||
archetypeSnapshot = new ArchetypeSnapshot();
|
entities = new List<Entity>();
|
||||||
ArchetypeSnapshots.Add(archetype.Signature, archetypeSnapshot);
|
Filters.Add(filter.Signature, entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
archetypeSnapshot.Take(archetype);
|
entities.Clear();
|
||||||
|
|
||||||
|
foreach (var entity in filter.EntitySet.AsSpan())
|
||||||
|
{
|
||||||
|
entities.Add(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TakeComponentSnapshot(TypeId typeId, ComponentStorage componentStorage)
|
private void TakeComponentSnapshot(TypeId typeId, ComponentStorage componentStorage)
|
||||||
|
@ -177,27 +190,6 @@ public class Snapshot
|
||||||
snapshot.Take(relationStorage);
|
snapshot.Take(relationStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ArchetypeSnapshot
|
|
||||||
{
|
|
||||||
private readonly NativeArray<Entity> Entities;
|
|
||||||
public int Count => Entities.Count;
|
|
||||||
|
|
||||||
public ArchetypeSnapshot()
|
|
||||||
{
|
|
||||||
Entities = new NativeArray<Entity>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Take(Archetype archetype)
|
|
||||||
{
|
|
||||||
archetype.Entities.CopyTo(Entities);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Restore(Archetype archetype)
|
|
||||||
{
|
|
||||||
Entities.CopyTo(archetype.Entities);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ComponentSnapshot
|
private class ComponentSnapshot
|
||||||
{
|
{
|
||||||
private readonly Dictionary<Entity, int> EntityIDToStorageIndex = new Dictionary<Entity, int>();
|
private readonly Dictionary<Entity, int> EntityIDToStorageIndex = new Dictionary<Entity, int>();
|
||||||
|
|
238
src/World.cs
238
src/World.cs
|
@ -5,7 +5,7 @@ using MoonTools.ECS.Collections;
|
||||||
|
|
||||||
namespace MoonTools.ECS
|
namespace MoonTools.ECS
|
||||||
{
|
{
|
||||||
public class World
|
public class World : IDisposable
|
||||||
{
|
{
|
||||||
// Get TypeId from a Type
|
// Get TypeId from a Type
|
||||||
private readonly Dictionary<Type, TypeId> TypeToId = new Dictionary<Type, TypeId>();
|
private readonly Dictionary<Type, TypeId> TypeToId = new Dictionary<Type, TypeId>();
|
||||||
|
@ -17,10 +17,9 @@ namespace MoonTools.ECS
|
||||||
// Get element size from a TypeId
|
// Get element size from a TypeId
|
||||||
private readonly Dictionary<TypeId, int> ElementSizes = new Dictionary<TypeId, int>();
|
private readonly Dictionary<TypeId, int> ElementSizes = new Dictionary<TypeId, int>();
|
||||||
|
|
||||||
// Archetypes
|
// Filters
|
||||||
internal readonly Dictionary<ArchetypeSignature, Archetype> ArchetypeIndex = new Dictionary<ArchetypeSignature, Archetype>();
|
internal readonly Dictionary<FilterSignature, Filter> FilterIndex = new Dictionary<FilterSignature, Filter>();
|
||||||
internal readonly Dictionary<Entity, ArchetypeRecord> EntityIndex = new Dictionary<Entity, ArchetypeRecord>();
|
private readonly Dictionary<TypeId, List<Filter>> TypeToFilter = new Dictionary<TypeId, List<Filter>>();
|
||||||
internal readonly Archetype EmptyArchetype;
|
|
||||||
|
|
||||||
// TODO: can we make the tag an native array of chars at some point?
|
// TODO: can we make the tag an native array of chars at some point?
|
||||||
internal Dictionary<Entity, string> EntityTags = new Dictionary<Entity, string>();
|
internal Dictionary<Entity, string> EntityTags = new Dictionary<Entity, string>();
|
||||||
|
@ -35,14 +34,12 @@ namespace MoonTools.ECS
|
||||||
public FilterBuilder FilterBuilder => new FilterBuilder(this);
|
public FilterBuilder FilterBuilder => new FilterBuilder(this);
|
||||||
|
|
||||||
internal readonly Dictionary<TypeId, ComponentStorage> ComponentIndex = new Dictionary<TypeId, ComponentStorage>();
|
internal readonly Dictionary<TypeId, ComponentStorage> ComponentIndex = new Dictionary<TypeId, ComponentStorage>();
|
||||||
|
internal Dictionary<Entity, IndexableSet<TypeId>> EntityComponentIndex = new Dictionary<Entity, IndexableSet<TypeId>>();
|
||||||
|
|
||||||
internal IdAssigner EntityIdAssigner = new IdAssigner();
|
internal IdAssigner EntityIdAssigner = new IdAssigner();
|
||||||
private IdAssigner TypeIdAssigner = new IdAssigner();
|
private IdAssigner TypeIdAssigner = new IdAssigner();
|
||||||
|
|
||||||
public World()
|
private bool IsDisposed;
|
||||||
{
|
|
||||||
EmptyArchetype = CreateArchetype(ArchetypeSignature.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal TypeId GetTypeId<T>() where T : unmanaged
|
internal TypeId GetTypeId<T>() where T : unmanaged
|
||||||
{
|
{
|
||||||
|
@ -51,7 +48,7 @@ namespace MoonTools.ECS
|
||||||
return TypeToId[typeof(T)];
|
return TypeToId[typeof(T)];
|
||||||
}
|
}
|
||||||
|
|
||||||
var typeId = new TypeId(TypeIdAssigner.Assign());
|
var typeId = new TypeId(TypeIdAssigner.Assign(out var _));
|
||||||
TypeToId.Add(typeof(T), typeId);
|
TypeToId.Add(typeof(T), typeId);
|
||||||
ElementSizes.Add(typeId, Unsafe.SizeOf<T>());
|
ElementSizes.Add(typeId, Unsafe.SizeOf<T>());
|
||||||
|
|
||||||
|
@ -62,6 +59,20 @@ namespace MoonTools.ECS
|
||||||
return typeId;
|
return typeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal TypeId GetComponentTypeId<T>() where T : unmanaged
|
||||||
|
{
|
||||||
|
var typeId = GetTypeId<T>();
|
||||||
|
if (ComponentIndex.TryGetValue(typeId, out var componentStorage))
|
||||||
|
{
|
||||||
|
return typeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentStorage = new ComponentStorage(typeId, ElementSizes[typeId]);
|
||||||
|
ComponentIndex.Add(typeId, componentStorage);
|
||||||
|
TypeToFilter.Add(typeId, new List<Filter>());
|
||||||
|
return typeId;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private ComponentStorage GetComponentStorage<T>() where T : unmanaged
|
private ComponentStorage GetComponentStorage<T>() where T : unmanaged
|
||||||
{
|
{
|
||||||
|
@ -71,29 +82,44 @@ namespace MoonTools.ECS
|
||||||
return componentStorage;
|
return componentStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentStorage = new ComponentStorage(ElementSizes[typeId]);
|
componentStorage = new ComponentStorage(typeId, ElementSizes[typeId]);
|
||||||
ComponentIndex.Add(typeId, componentStorage);
|
ComponentIndex.Add(typeId, componentStorage);
|
||||||
|
TypeToFilter.Add(typeId, new List<Filter>());
|
||||||
return componentStorage;
|
return componentStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Archetype CreateArchetype(ArchetypeSignature signature)
|
// FILTERS
|
||||||
|
|
||||||
|
internal Filter GetFilter(FilterSignature signature)
|
||||||
{
|
{
|
||||||
var archetype = new Archetype(this, signature);
|
if (!FilterIndex.TryGetValue(signature, out var filter))
|
||||||
ArchetypeIndex.Add(signature, archetype);
|
{
|
||||||
return archetype;
|
filter = new Filter(this, signature);
|
||||||
|
|
||||||
|
foreach (var typeId in signature.Included)
|
||||||
|
{
|
||||||
|
TypeToFilter[typeId].Add(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var typeId in signature.Excluded)
|
||||||
|
{
|
||||||
|
TypeToFilter[typeId].Add(filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ENTITIES
|
// ENTITIES
|
||||||
|
|
||||||
public Entity CreateEntity(string tag = "")
|
public Entity CreateEntity(string tag = "")
|
||||||
{
|
{
|
||||||
var entity = new Entity(EntityIdAssigner.Assign());
|
var entity = new Entity(EntityIdAssigner.Assign(out var recycled));
|
||||||
EntityIndex.Add(entity, new ArchetypeRecord(EmptyArchetype, EmptyArchetype.Count));
|
|
||||||
EmptyArchetype.Append(entity);
|
|
||||||
|
|
||||||
if (!EntityRelationIndex.ContainsKey(entity))
|
if (!recycled)
|
||||||
{
|
{
|
||||||
EntityRelationIndex.Add(entity, new IndexableSet<TypeId>());
|
EntityRelationIndex.Add(entity, new IndexableSet<TypeId>());
|
||||||
|
EntityComponentIndex.Add(entity, new IndexableSet<TypeId>());
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityTags[entity] = tag;
|
EntityTags[entity] = tag;
|
||||||
|
@ -113,15 +139,16 @@ namespace MoonTools.ECS
|
||||||
|
|
||||||
public void Destroy(in Entity entity)
|
public void Destroy(in Entity entity)
|
||||||
{
|
{
|
||||||
var record = EntityIndex[entity];
|
|
||||||
var archetype = record.Archetype;
|
|
||||||
var row = record.Row;
|
|
||||||
|
|
||||||
// remove all components from storages
|
// remove all components from storages
|
||||||
for (int i = 0; i < archetype.Signature.Count; i += 1)
|
foreach (var componentTypeIndex in EntityComponentIndex[entity])
|
||||||
{
|
{
|
||||||
var componentStorage = ComponentIndex[archetype.Signature[i]];
|
var componentStorage = ComponentIndex[componentTypeIndex];
|
||||||
componentStorage.Remove(entity);
|
componentStorage.Remove(entity);
|
||||||
|
|
||||||
|
foreach (var filter in TypeToFilter[componentTypeIndex])
|
||||||
|
{
|
||||||
|
filter.RemoveEntity(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove all relations from storage
|
// remove all relations from storage
|
||||||
|
@ -131,18 +158,9 @@ namespace MoonTools.ECS
|
||||||
relationStorage.RemoveEntity(entity);
|
relationStorage.RemoveEntity(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EntityComponentIndex[entity].Clear();
|
||||||
EntityRelationIndex[entity].Clear();
|
EntityRelationIndex[entity].Clear();
|
||||||
|
|
||||||
// remove from archetype
|
|
||||||
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();
|
|
||||||
EntityIndex.Remove(entity);
|
|
||||||
|
|
||||||
// recycle ID
|
// recycle ID
|
||||||
EntityIdAssigner.Unassign(entity.ID);
|
EntityIdAssigner.Unassign(entity.ID);
|
||||||
}
|
}
|
||||||
|
@ -155,6 +173,11 @@ namespace MoonTools.ECS
|
||||||
return storage.Has(entity);
|
return storage.Has(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal bool Has(in Entity entity, in TypeId typeId)
|
||||||
|
{
|
||||||
|
return EntityComponentIndex[entity].Contains(typeId);
|
||||||
|
}
|
||||||
|
|
||||||
public bool Some<T>() where T : unmanaged
|
public bool Some<T>() where T : unmanaged
|
||||||
{
|
{
|
||||||
var storage = GetComponentStorage<T>();
|
var storage = GetComponentStorage<T>();
|
||||||
|
@ -185,7 +208,12 @@ namespace MoonTools.ECS
|
||||||
|
|
||||||
if (!componentStorage.Set(entity, component))
|
if (!componentStorage.Set(entity, component))
|
||||||
{
|
{
|
||||||
TransferArchetype(entity, FindArchetypeByAdd<T>(entity));
|
EntityComponentIndex[entity].Add(componentStorage.TypeId);
|
||||||
|
|
||||||
|
foreach (var filter in TypeToFilter[componentStorage.TypeId])
|
||||||
|
{
|
||||||
|
filter.Check(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,89 +223,15 @@ namespace MoonTools.ECS
|
||||||
|
|
||||||
if (componentStorage.Remove(entity))
|
if (componentStorage.Remove(entity))
|
||||||
{
|
{
|
||||||
TransferArchetype(entity, FindArchetypeByRemove<T>(entity));
|
EntityComponentIndex[entity].Remove(componentStorage.TypeId);
|
||||||
|
|
||||||
|
foreach (var filter in TypeToFilter[componentStorage.TypeId])
|
||||||
|
{
|
||||||
|
filter.Check(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Archetype FindArchetypeByAdd<T>(in Entity entity)
|
|
||||||
{
|
|
||||||
var componentTypeId = TypeToId[typeof(T)];
|
|
||||||
var record = EntityIndex[entity];
|
|
||||||
var archetype = record.Archetype;
|
|
||||||
|
|
||||||
if (archetype.AddEdges.TryGetValue(componentTypeId, out var nextArchetype))
|
|
||||||
{
|
|
||||||
return nextArchetype;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (!nextArchetype.RemoveEdges.ContainsKey(componentTypeId))
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (!nextArchetype.AddEdges.ContainsKey(componentTypeId))
|
|
||||||
{
|
|
||||||
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
|
||||||
|
|
||||||
private RelationStorage RegisterRelationType(TypeId typeId)
|
private RelationStorage RegisterRelationType(TypeId typeId)
|
||||||
|
@ -450,7 +404,7 @@ namespace MoonTools.ECS
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
public ComponentTypeEnumerator Debug_GetAllComponentTypes(Entity entity)
|
public ComponentTypeEnumerator Debug_GetAllComponentTypes(Entity entity)
|
||||||
{
|
{
|
||||||
return new ComponentTypeEnumerator(this, EntityIndex[entity]);
|
return new ComponentTypeEnumerator(this, EntityComponentIndex[entity]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Entity> Debug_GetEntities(Type componentType)
|
public IEnumerable<Entity> Debug_GetEntities(Type componentType)
|
||||||
|
@ -473,29 +427,67 @@ namespace MoonTools.ECS
|
||||||
public ref struct ComponentTypeEnumerator
|
public ref struct ComponentTypeEnumerator
|
||||||
{
|
{
|
||||||
private World World;
|
private World World;
|
||||||
private ArchetypeRecord Record;
|
private IndexableSet<TypeId> Types;
|
||||||
private int ComponentIndex;
|
private int ComponentIndex;
|
||||||
|
|
||||||
public ComponentTypeEnumerator GetEnumerator() => this;
|
public ComponentTypeEnumerator GetEnumerator() => this;
|
||||||
|
|
||||||
internal ComponentTypeEnumerator(
|
internal ComponentTypeEnumerator(
|
||||||
World world,
|
World world,
|
||||||
ArchetypeRecord record
|
IndexableSet<TypeId> types
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
World = world;
|
World = world;
|
||||||
Record = record;
|
Types = types;
|
||||||
ComponentIndex = -1;
|
ComponentIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool MoveNext()
|
public bool MoveNext()
|
||||||
{
|
{
|
||||||
ComponentIndex += 1;
|
ComponentIndex += 1;
|
||||||
return ComponentIndex < Record.Archetype.Signature.Count;
|
return ComponentIndex < Types.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe Type Current => World.IdToType[Record.Archetype.Signature[ComponentIndex]];
|
public unsafe Type Current => World.IdToType[Types[ComponentIndex]];
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!IsDisposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// TODO: dispose managed state (managed objects)
|
||||||
|
foreach (var componentStorage in ComponentIndex.Values)
|
||||||
|
{
|
||||||
|
componentStorage.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var relationStorage in RelationIndex.Values)
|
||||||
|
{
|
||||||
|
relationStorage.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: free unmanaged resources (unmanaged objects) and override finalizer
|
||||||
|
// TODO: set large fields to null
|
||||||
|
IsDisposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
|
||||||
|
// ~World()
|
||||||
|
// {
|
||||||
|
// // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
// Dispose(disposing: false);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue