Entity Iteration Rework (#1)

Filters are now the canonical way to look up Entities. The only exception is the new GetSingletonEntity.
pull/2/head
cosmonaut 2022-04-03 21:46:52 +00:00
parent b4c862d568
commit d7e795309f
4 changed files with 30 additions and 40 deletions

View File

@ -10,11 +10,18 @@ internal class ComponentDepot
private Dictionary<int, HashSet<Type>> entityComponentMap = new Dictionary<int, HashSet<Type>>();
#if DEBUG
private Dictionary<Type, Filter> singleComponentFilters = new Dictionary<Type, Filter>();
#endif
internal void Register<TComponent>() where TComponent : struct
{
if (!storages.ContainsKey(typeof(TComponent)))
{
storages.Add(typeof(TComponent), new ComponentStorage<TComponent>());
#if DEBUG
singleComponentFilters.Add(typeof(TComponent), CreateFilter(new HashSet<Type>() { typeof(TComponent) }, new HashSet<Type>()));
#endif
}
}
@ -79,14 +86,9 @@ internal class ComponentDepot
}
}
public ref readonly Entity GetEntity<TComponent>() where TComponent : struct
public Entity GetSingletonEntity<TComponent>() where TComponent : struct
{
return ref Lookup<TComponent>().FirstEntity();
}
public ReadOnlySpan<Entity> ReadEntities<TComponent>() where TComponent : struct
{
return Lookup<TComponent>().AllEntities();
return Lookup<TComponent>().FirstEntity();
}
public ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
@ -226,8 +228,7 @@ internal class ComponentDepot
filterSignatureToEntityIDs[filterSignature].Add(entityID);
}
// debug use only!
#if DEBUG
public IEnumerable<object> Debug_GetAllComponents(int entityID)
{
foreach (var (type, storage) in storages)
@ -239,9 +240,9 @@ internal class ComponentDepot
}
}
public ReadOnlySpan<Entity> Debug_GetEntities(Type componentType)
public IEnumerable<Entity> Debug_GetEntities(Type componentType)
{
return Lookup(componentType).AllEntities();
return singleComponentFilters[componentType].Entities;
}
public IEnumerable<Type> Debug_SearchComponentType(string typeString)
@ -254,4 +255,5 @@ internal class ComponentDepot
}
}
}
#endif
}

View File

@ -4,8 +4,6 @@ internal abstract class ComponentStorage
{
public abstract bool Has(int entityID);
public abstract void Remove(int entityID);
public abstract ReadOnlySpan<Entity> AllEntities();
public abstract object Debug_Get(int entityID);
}
@ -13,10 +11,9 @@ internal abstract class ComponentStorage
internal class ComponentStorage<TComponent> : ComponentStorage where TComponent : struct
{
private int nextID;
private IDStorage idStorage = new IDStorage();
private readonly Dictionary<int, int> entityIDToStorageIndex = new Dictionary<int, int>();
private Entity[] storageIndexToEntities = new Entity[64];
private TComponent[] components = new TComponent[64];
private readonly Dictionary<int, int> entityIDToStorageIndex = new Dictionary<int, int>(16);
private int[] entityIDs = new int[16];
private TComponent[] components = new TComponent[16];
public bool Any()
{
@ -59,11 +56,11 @@ internal class ComponentStorage<TComponent> : ComponentStorage where TComponent
if (index >= components.Length)
{
Array.Resize(ref components, components.Length * 2);
Array.Resize(ref storageIndexToEntities, storageIndexToEntities.Length * 2);
Array.Resize(ref entityIDs, entityIDs.Length * 2);
}
entityIDToStorageIndex[entityID] = index;
storageIndexToEntities[index] = new Entity(entityID);
entityIDs[index] = entityID;
}
components[entityIDToStorageIndex[entityID]] = component;
@ -79,13 +76,12 @@ internal class ComponentStorage<TComponent> : ComponentStorage where TComponent
var lastElementIndex = nextID - 1;
// move a component into the hole to maintain contiguous memory
if (entityIDToStorageIndex.Count > 0 && storageIndex != lastElementIndex)
if (lastElementIndex != storageIndex)
{
var lastEntity = storageIndexToEntities[lastElementIndex];
entityIDToStorageIndex[lastEntity.ID] = storageIndex;
storageIndexToEntities[storageIndex] = lastEntity;
var lastEntityID = entityIDs[lastElementIndex];
entityIDToStorageIndex[lastEntityID] = storageIndex;
components[storageIndex] = components[lastElementIndex];
entityIDs[storageIndex] = lastEntityID;
}
nextID -= 1;
@ -98,18 +94,13 @@ internal class ComponentStorage<TComponent> : ComponentStorage where TComponent
entityIDToStorageIndex.Clear();
}
public override ReadOnlySpan<Entity> AllEntities()
{
return new ReadOnlySpan<Entity>(storageIndexToEntities, 0, nextID);
}
public ReadOnlySpan<TComponent> AllComponents()
{
return new ReadOnlySpan<TComponent>(components, 0, nextID);
}
public ref readonly Entity FirstEntity()
public Entity FirstEntity()
{
return ref storageIndexToEntities[0];
return new Entity(entityIDs[0]);
}
}

View File

@ -1,5 +1,7 @@
// NOTE: these methods are very inefficient
// this class should only be used in debugging contexts!!
#if DEBUG
namespace MoonTools.ECS
{
public abstract class DebugSystem : System
@ -13,7 +15,7 @@ namespace MoonTools.ECS
return ComponentDepot.Debug_GetAllComponents(entity.ID);
}
protected ReadOnlySpan<Entity> Debug_GetEntities(Type componentType)
protected IEnumerable<Entity> Debug_GetEntities(Type componentType)
{
return ComponentDepot.Debug_GetEntities(componentType);
}
@ -24,3 +26,4 @@ namespace MoonTools.ECS
}
}
}
#endif

View File

@ -16,12 +16,6 @@ public abstract class EntityComponentReader
ComponentDepot = componentDepot;
}
// TODO: is this faster or slower than a single-component Filter?
protected ReadOnlySpan<Entity> ReadEntities<TComponent>() where TComponent : struct
{
return ComponentDepot.ReadEntities<TComponent>();
}
protected ReadOnlySpan<TComponent> ReadComponents<TComponent>() where TComponent : struct
{
return ComponentDepot.ReadComponents<TComponent>();
@ -47,9 +41,9 @@ public abstract class EntityComponentReader
return ref ComponentDepot.Get<TComponent>();
}
protected ref readonly Entity GetEntity<TComponent>() where TComponent : struct
protected Entity GetSingletonEntity<TComponent>() where TComponent : struct
{
return ref ComponentDepot.GetEntity<TComponent>();
return ComponentDepot.GetSingletonEntity<TComponent>();
}
protected bool Exists(in Entity entity)