2022-04-08 05:52:03 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2022-05-03 04:51:11 +00:00
|
|
|
|
using System.Runtime.InteropServices;
|
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
|
|
|
|
{
|
2022-04-08 05:52:03 +00:00
|
|
|
|
public abstract bool Has(int entityID);
|
2022-05-03 04:51:11 +00:00
|
|
|
|
public abstract bool Remove(int entityID);
|
2022-04-08 05:52:03 +00:00
|
|
|
|
public abstract object Debug_Get(int entityID);
|
2022-05-03 04:51:11 +00:00
|
|
|
|
public abstract ComponentStorageState CreateState();
|
|
|
|
|
public abstract void Save(ComponentStorageState state);
|
|
|
|
|
public abstract void Load(ComponentStorageState state);
|
2022-03-05 02:01:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-03 04:51:11 +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 override bool Has(int entityID)
|
|
|
|
|
{
|
|
|
|
|
return entityIDToStorageIndex.ContainsKey(entityID);
|
|
|
|
|
}
|
2022-03-25 19:32:35 +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
|
|
|
|
}
|
|
|
|
|
|
2022-04-08 05:52:03 +00:00
|
|
|
|
public override object Debug_Get(int entityID)
|
2022-03-05 02:01:44 +00:00
|
|
|
|
{
|
2022-04-08 05:52:03 +00:00
|
|
|
|
return components[entityIDToStorageIndex[entityID]];
|
|
|
|
|
}
|
2022-03-05 02:01:44 +00:00
|
|
|
|
|
2022-04-08 05:52:03 +00:00
|
|
|
|
public ref readonly TComponent Get()
|
|
|
|
|
{
|
|
|
|
|
#if DEBUG
|
|
|
|
|
if (nextID == 0)
|
2022-03-05 02:01:44 +00:00
|
|
|
|
{
|
2022-04-08 05:52:03 +00:00
|
|
|
|
throw new ArgumentOutOfRangeException("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
|
|
|
|
}
|
|
|
|
|
|
2022-05-03 04:51:11 +00:00
|
|
|
|
// Returns true if the entity already had this component.
|
|
|
|
|
public bool Set(int entityID, in TComponent component)
|
2022-03-05 02:01:44 +00:00
|
|
|
|
{
|
2022-05-03 04:51:11 +00:00
|
|
|
|
bool result = true;
|
|
|
|
|
|
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-05-03 04:51:11 +00:00
|
|
|
|
|
|
|
|
|
result = false;
|
2022-03-05 02:01:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-04-08 05:52:03 +00:00
|
|
|
|
components[entityIDToStorageIndex[entityID]] = component;
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
|
|
|
|
return result;
|
2022-03-05 02:01:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-03 04:51:11 +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.ContainsKey(entityID))
|
|
|
|
|
{
|
|
|
|
|
var storageIndex = entityIDToStorageIndex[entityID];
|
|
|
|
|
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;
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
2022-04-08 05:52:03 +00:00
|
|
|
|
}
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
|
|
|
|
return false;
|
2022-04-08 05:52:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Clear()
|
|
|
|
|
{
|
|
|
|
|
nextID = 0;
|
|
|
|
|
entityIDToStorageIndex.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ReadOnlySpan<TComponent> AllComponents()
|
|
|
|
|
{
|
|
|
|
|
return new ReadOnlySpan<TComponent>(components, 0, nextID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Entity FirstEntity()
|
|
|
|
|
{
|
|
|
|
|
return new Entity(entityIDs[0]);
|
|
|
|
|
}
|
2022-05-03 04:51:11 +00:00
|
|
|
|
|
|
|
|
|
public override ComponentStorageState CreateState()
|
|
|
|
|
{
|
|
|
|
|
return ComponentStorageState.Create<TComponent>(nextID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void Save(ComponentStorageState state)
|
|
|
|
|
{
|
|
|
|
|
ReadOnlySpan<byte> entityIDBytes = MemoryMarshal.Cast<int, byte>(new ReadOnlySpan<int>(entityIDs, 0, nextID));
|
|
|
|
|
|
|
|
|
|
if (entityIDBytes.Length > state.EntityIDs.Length)
|
|
|
|
|
{
|
|
|
|
|
Array.Resize(ref state.EntityIDs, entityIDBytes.Length);
|
|
|
|
|
}
|
|
|
|
|
entityIDBytes.CopyTo(state.EntityIDs);
|
|
|
|
|
|
|
|
|
|
ReadOnlySpan<byte> componentBytes = MemoryMarshal.Cast<TComponent, byte>(AllComponents());
|
|
|
|
|
if (componentBytes.Length > state.Components.Length)
|
|
|
|
|
{
|
|
|
|
|
Array.Resize(ref state.Components, componentBytes.Length);
|
|
|
|
|
}
|
|
|
|
|
componentBytes.CopyTo(state.Components);
|
|
|
|
|
|
|
|
|
|
state.Count = nextID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void Load(ComponentStorageState state)
|
|
|
|
|
{
|
|
|
|
|
state.EntityIDs.CopyTo(MemoryMarshal.Cast<int, byte>(entityIDs));
|
|
|
|
|
state.Components.CopyTo(MemoryMarshal.Cast<TComponent, byte>(components));
|
|
|
|
|
|
|
|
|
|
entityIDToStorageIndex.Clear();
|
|
|
|
|
for (var i = 0; i < state.Count; i += 1)
|
|
|
|
|
{
|
|
|
|
|
entityIDToStorageIndex[entityIDs[i]] = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nextID = state.Count;
|
|
|
|
|
}
|
2022-03-21 23:21:42 +00:00
|
|
|
|
}
|
2022-03-05 02:01:44 +00:00
|
|
|
|
}
|