MoonTools.ECS/src/ComponentStorage.cs

149 lines
3.4 KiB
C#
Raw Normal View History

2022-04-08 05:52:03 +00:00
using System;
using System.Collections.Generic;
2022-03-05 02:01:44 +00:00
2022-04-08 05:52:03 +00:00
namespace MoonTools.ECS
2022-03-05 02:01:44 +00:00
{
2022-04-08 05:52:03 +00:00
internal abstract class ComponentStorage
2022-03-05 02:01:44 +00:00
{
internal abstract unsafe void Set(int entityID, void* component);
public abstract bool Remove(int entityID);
public abstract void Clear();
// used for debugging and template instantiation
internal abstract unsafe void* UntypedGet(int entityID);
// used to create correctly typed storage on snapshot
public abstract ComponentStorage CreateStorage();
#if DEBUG
internal abstract object Debug_Get(int entityID);
internal abstract IEnumerable<int> Debug_GetEntityIDs();
#endif
2022-03-05 02:01:44 +00:00
}
internal class ComponentStorage<TComponent> : ComponentStorage where TComponent : unmanaged
2022-03-05 02:01:44 +00:00
{
2022-04-08 05:52:03 +00:00
private int nextID;
private readonly Dictionary<int, int> entityIDToStorageIndex = new Dictionary<int, int>(16);
private int[] entityIDs = new int[16];
private TComponent[] components = new TComponent[16];
2022-03-05 02:01:44 +00:00
2022-04-08 05:52:03 +00:00
public bool Any()
{
return nextID > 0;
}
2022-03-05 02:01:44 +00:00
2022-04-08 05:52:03 +00:00
public ref readonly TComponent Get(int entityID)
2022-03-21 23:21:42 +00:00
{
2022-04-08 05:52:03 +00:00
return ref components[entityIDToStorageIndex[entityID]];
2022-03-21 23:21:42 +00:00
}
internal override unsafe void* UntypedGet(int entityID)
2022-03-05 02:01:44 +00:00
{
fixed (void* p = &components[entityIDToStorageIndex[entityID]])
{
return p;
}
2022-04-08 05:52:03 +00:00
}
2022-03-05 02:01:44 +00:00
public ref readonly TComponent GetFirst()
2022-04-08 05:52:03 +00:00
{
#if DEBUG
if (nextID == 0)
2022-03-05 02:01:44 +00:00
{
throw new IndexOutOfRangeException("Component storage is empty!");
2022-03-05 02:01:44 +00:00
}
2022-04-08 05:52:03 +00:00
#endif
return ref components[0];
2022-03-05 02:01:44 +00:00
}
public void Set(int entityID, in TComponent component)
2022-03-05 02:01:44 +00:00
{
2022-04-08 05:52:03 +00:00
if (!entityIDToStorageIndex.ContainsKey(entityID))
{
var index = nextID;
nextID += 1;
2022-03-05 02:01:44 +00:00
2022-04-08 05:52:03 +00:00
if (index >= components.Length)
{
Array.Resize(ref components, components.Length * 2);
Array.Resize(ref entityIDs, entityIDs.Length * 2);
}
2022-03-05 02:01:44 +00:00
2022-04-08 05:52:03 +00:00
entityIDToStorageIndex[entityID] = index;
entityIDs[index] = entityID;
2022-03-05 02:01:44 +00:00
}
2022-04-08 05:52:03 +00:00
components[entityIDToStorageIndex[entityID]] = component;
}
internal override unsafe void Set(int entityID, void* component)
{
Set(entityID, *((TComponent*) component));
2022-03-05 02:01:44 +00:00
}
// Returns true if the entity had this component.
public override bool Remove(int entityID)
2022-04-08 05:52:03 +00:00
{
if (entityIDToStorageIndex.TryGetValue(entityID, out int storageIndex))
2022-04-08 05:52:03 +00:00
{
entityIDToStorageIndex.Remove(entityID);
2022-03-05 02:01:44 +00:00
2022-04-08 05:52:03 +00:00
var lastElementIndex = nextID - 1;
2022-03-21 23:21:42 +00:00
2022-04-08 05:52:03 +00:00
// move a component into the hole to maintain contiguous memory
if (lastElementIndex != storageIndex)
{
var lastEntityID = entityIDs[lastElementIndex];
entityIDToStorageIndex[lastEntityID] = storageIndex;
components[storageIndex] = components[lastElementIndex];
entityIDs[storageIndex] = lastEntityID;
}
nextID -= 1;
return true;
2022-04-08 05:52:03 +00:00
}
return false;
2022-04-08 05:52:03 +00:00
}
public override void Clear()
2022-04-08 05:52:03 +00:00
{
nextID = 0;
entityIDToStorageIndex.Clear();
}
public ReadOnlySpan<TComponent> AllComponents()
{
return new ReadOnlySpan<TComponent>(components, 0, nextID);
}
public Entity FirstEntity()
{
#if DEBUG
if (nextID == 0)
{
throw new IndexOutOfRangeException("Component storage is empty!");
}
#endif
2022-04-08 05:52:03 +00:00
return new Entity(entityIDs[0]);
}
public override ComponentStorage<TComponent> CreateStorage()
{
return new ComponentStorage<TComponent>();
}
#if DEBUG
internal override object Debug_Get(int entityID)
{
return components[entityIDToStorageIndex[entityID]];
}
internal override IEnumerable<int> Debug_GetEntityIDs()
{
return entityIDToStorageIndex.Keys;
}
#endif
2022-03-21 23:21:42 +00:00
}
2022-03-05 02:01:44 +00:00
}