diff --git a/src/Debug/Attributes/DebugColorAttribute.cs b/src/Debug/Attributes/DebugColorAttribute.cs
index 5ec68dd..fda3aa5 100644
--- a/src/Debug/Attributes/DebugColorAttribute.cs
+++ b/src/Debug/Attributes/DebugColorAttribute.cs
@@ -61,7 +61,6 @@ namespace DCFApixels.DragonECS
/// color code Black. RGB is (0, 0, 0)
public const int Black = 0;
-
[FieldOffset(0)] public readonly int colorCode;
[FieldOffset(3)] public readonly byte r;
[FieldOffset(2)] public readonly byte g;
@@ -99,6 +98,5 @@ namespace DCFApixels.DragonECS
{
return new DebugColor((byte)(a.r / b), (byte)(a.g / b), (byte)(a.b / b));
}
- //public static explicit operator DebugColor(int colorCode) => new DebugColor(colorCode);
}
}
\ No newline at end of file
diff --git a/src/Pools/EcsHybridPool.cs b/src/Pools/EcsHybridPool.cs
new file mode 100644
index 0000000..2653295
--- /dev/null
+++ b/src/Pools/EcsHybridPool.cs
@@ -0,0 +1,223 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace DCFApixels.DragonECS
+{
+ /// Pool for IEcsComponent components
+ public sealed class EcsHybridPool : IEcsPoolImplementation, IEcsHybridPool, IEnumerable //IEnumerable - IntelliSense hack
+ where T : IEcsHybridComponent
+ {
+ private EcsWorld _source;
+ private int _componentID;
+
+ private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID
+ private T[] _items; //dense
+ private int _itemsCount;
+ private int[] _recycledItems;
+ private int _recycledItemsCount;
+
+ private IEcsComponentReset _componentResetHandler = EcsComponentResetHandler.instance;
+ private IEcsComponentCopy _componentCopyHandler = EcsComponentCopyHandler.instance;
+
+ private List _listeners = new List();
+
+ #region Properites
+ public int Count => _itemsCount;
+ public int Capacity => _items.Length;
+ public int ComponentID => _componentID;
+ public Type ComponentType => typeof(T);
+ public EcsWorld World => _source;
+ #endregion
+
+ #region Init
+ void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
+ {
+ _source = world;
+ _componentID = componentID;
+
+ const int capacity = 512;
+
+ _mapping = new int[world.Capacity];
+ _recycledItems = new int[128];
+ _recycledItemsCount = 0;
+ _items = new T[capacity];
+ _itemsCount = 0;
+ }
+ #endregion
+
+ #region Methods
+ public void Add(int entityID, T component)
+ {
+ ref int itemIndex = ref _mapping[entityID];
+#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
+ if (itemIndex > 0) EcsPoolThrowHalper.ThrowAlreadyHasComponent(entityID);
+#endif
+ if (_recycledItemsCount > 0)
+ {
+ itemIndex = _recycledItems[--_recycledItemsCount];
+ _itemsCount++;
+ }
+ else
+ {
+ itemIndex = ++_itemsCount;
+ if (itemIndex >= _items.Length)
+ Array.Resize(ref _items, _items.Length << 1);
+ }
+ this.IncrementEntityComponentCount(entityID);
+ _listeners.InvokeOnAdd(entityID);
+ _items[itemIndex] = component;
+ }
+ public void Set(int entityID, T component)
+ {
+ ref int itemIndex = ref _mapping[entityID];
+ if(itemIndex <= 0)
+ {//null
+ if (_recycledItemsCount > 0)
+ {
+ itemIndex = _recycledItems[--_recycledItemsCount];
+ _itemsCount++;
+ }
+ else
+ {
+ itemIndex = ++_itemsCount;
+ if (itemIndex >= _items.Length)
+ Array.Resize(ref _items, _items.Length << 1);
+ }
+ this.IncrementEntityComponentCount(entityID);
+ }
+ else
+ {//not null
+ _listeners.InvokeOnDel(entityID);
+ if (_items[itemIndex] is IDisposable disposable)
+ disposable.Dispose();
+ }
+ _listeners.InvokeOnAdd(entityID);
+ _items[itemIndex] = component;
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T Get(int entityID)
+ {
+#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
+ if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID);
+#endif
+ _listeners.InvokeOnGet(entityID);
+ return _items[_mapping[entityID]];
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref readonly T Read(int entityID)
+ {
+#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
+ if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID);
+#endif
+ return ref _items[_mapping[entityID]];
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool Has(int entityID)
+ {
+ return _mapping[entityID] > 0;
+ }
+ public void Del(int entityID)
+ {
+#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
+ if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(entityID);
+#endif
+ ref int itemIndex = ref _mapping[entityID];
+ T component = _items[itemIndex];
+ if (component is IDisposable disposable)
+ disposable.Dispose();
+ if (_recycledItemsCount >= _recycledItems.Length)
+ Array.Resize(ref _recycledItems, _recycledItems.Length << 1);
+ _recycledItems[_recycledItemsCount++] = itemIndex;
+ _mapping[entityID] = 0;
+ _itemsCount--;
+ this.DecrementEntityComponentCount(entityID);
+ _listeners.InvokeOnDel(entityID);
+ }
+ public void TryDel(int entityID)
+ {
+ if (Has(entityID)) Del(entityID);
+ }
+ public void Copy(int fromEntityID, int toEntityID)
+ {
+#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
+ if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID);
+#endif
+ Set(toEntityID, Get(fromEntityID));
+ }
+ public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID)
+ {
+#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
+ if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent(fromEntityID);
+#endif
+ toWorld.GetPool().Set(toEntityID, Get(fromEntityID));
+ }
+ #endregion
+
+ #region Callbacks
+ void IEcsPoolImplementation.OnWorldResize(int newSize)
+ {
+ Array.Resize(ref _mapping, newSize);
+ }
+ void IEcsPoolImplementation.OnWorldDestroy() { }
+ void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan buffer)
+ {
+ foreach (var entityID in buffer)
+ TryDel(entityID);
+ }
+ #endregion
+
+ #region Other
+ void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID, (T)dataRaw);
+ object IEcsPool.GetRaw(int entityID) => Read(entityID);
+ void IEcsPool.SetRaw(int entityID, object dataRaw) => Set(entityID, (T)dataRaw);
+ #endregion
+
+ #region Listeners
+ public void AddListener(IEcsPoolEventListener listener)
+ {
+ if (listener == null) { throw new ArgumentNullException("listener is null"); }
+ _listeners.Add(listener);
+ }
+ public void RemoveListener(IEcsPoolEventListener listener)
+ {
+ if (listener == null) { throw new ArgumentNullException("listener is null"); }
+ _listeners.Remove(listener);
+ }
+ #endregion
+
+ #region IEnumerator - IntelliSense hack
+ IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
+ IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
+ #endregion
+ }
+ /// Hybrid component
+ public interface IEcsHybridComponent
+ {
+ bool IsAlive { get; }
+ entlong Entity { set; }
+ void OnAddToPool();
+ void OnDelFromPool();
+ }
+ public static class EcsHybridPoolExt
+ {
+ public static EcsHybridPool GetPool(this EcsWorld self) where T : IEcsHybridComponent
+ {
+ return self.GetPool>();
+ }
+
+ public static EcsHybridPool Include(this EcsAspectBuilderBase self) where T : IEcsHybridComponent
+ {
+ return self.Include>();
+ }
+ public static EcsHybridPool Exclude(this EcsAspectBuilderBase self) where T : IEcsHybridComponent
+ {
+ return self.Exclude>();
+ }
+ public static EcsHybridPool Optional(this EcsAspectBuilderBase self) where T : IEcsHybridComponent
+ {
+ return self.Optional>();
+ }
+ }
+}
diff --git a/src/Pools/EcsPoolBase.cs b/src/Pools/EcsPoolBase.cs
index 459c01f..bcd8b90 100644
--- a/src/Pools/EcsPoolBase.cs
+++ b/src/Pools/EcsPoolBase.cs
@@ -41,6 +41,11 @@ namespace DCFApixels.DragonECS
T Add(int entityID);
T Get(int entityID);
}
+ public interface IEcsHybridPool : IEcsPool
+ {
+ void Add(int entityID, T component);
+ T Get(int entityID);
+ }
/// Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool.
public interface IEcsPoolImplementation : IEcsPool
{