using System; using Unity.Collections; using System.Diagnostics; namespace GameCore.TinyECS { public struct EntityCollection : IDisposable where TEnum : unmanaged { private struct Buffer { public int index; public int generation; } private NativeList entityBuffer; private NativeList entityIndexToDataIndex; private TEnum entityType; public EntityCollection(TEnum type, int capacity, Allocator allocator) { entityType = type; entityBuffer = new (capacity, allocator); entityIndexToDataIndex = new (capacity, allocator); } public void Dispose() { entityBuffer.Dispose(); entityIndexToDataIndex.Dispose(); } public int Count => entityBuffer.Length; public Entity this[int index] { get { var buf = entityBuffer[index]; return new(entityType, buf.index, buf.generation); } } public int Add(Entity entity) { CheckEntityType(entity); var dataIndex = entityBuffer.Length; var oldEntityCount = entityIndexToDataIndex.Length; if (entity.index >= oldEntityCount) { entityIndexToDataIndex.Resize(entity.index + 1, NativeArrayOptions.UninitializedMemory); for (int i = oldEntityCount, e = entity.index - 1; i < e; ++i) { entityIndexToDataIndex[i] = -1; } } entityIndexToDataIndex[entity.index] = dataIndex; entityBuffer.Add(new Buffer{index = entity.index, generation = entity.generation }); return dataIndex; } public bool TryGetIndex(Entity entity, out int index) { CheckEntityType(entity); index = 0; if (entity.index >= entityIndexToDataIndex.Length) { return false; } index = entityIndexToDataIndex[entity.index]; if (index < 0 || index >= entityBuffer.Length) { return false; } var buf = entityBuffer[index]; return buf.index == entity.index && buf.generation == entity.generation; } public bool Remove(Entity entity, out int index, out bool removeFromBack) { CheckEntityType(entity); if (!TryGetIndex(entity, out index)) { removeFromBack = false; return false; } removeFromBack = RemoveInternal(entity, index); return true; } private bool RemoveInternal(Entity entity, int index) { entityIndexToDataIndex[entity.index] = -1; var backIndex = entityBuffer.Length - 1; if (index == backIndex) { entityBuffer.Resize(backIndex, NativeArrayOptions.UninitializedMemory); return true; } var backBuf = entityBuffer[backIndex]; entityBuffer.RemoveAtSwapBack(index); entityIndexToDataIndex[backBuf.index] = index; return false; } [Conditional("DEBUG")] void CheckEntityType(Entity entity) { if (!entity.type.Equals(entityType)) throw new Exception($"Wrong entity type: {entity.type}"); } public void Clear() { entityBuffer.Clear(); entityIndexToDataIndex.Clear(); } } }