diff --git a/src/EcsGroup.cs b/src/EcsGroup.cs index 9a25f35..ee0a76e 100644 --- a/src/EcsGroup.cs +++ b/src/EcsGroup.cs @@ -2,7 +2,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Unity.Profiling; -using static UnityEngine.Networking.UnityWebRequest; using delayedOp = System.Int32; namespace DCFApixels.DragonECS @@ -38,6 +37,11 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _source.CapacitySparce; } + public bool IsReleazed + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _source.IsReleazed; + } #endregion #region Methods @@ -46,10 +50,10 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public EcsGroup.Enumerator GetEnumerator() => _source.GetEnumerator(); - public EcsGroup Extract() - { - return new EcsGroup(_source); - } + /// Equivalent of the EcsGroup.Clone() method + /// An editable clone of this EcsReadnolyGroup + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EcsGroup Extract() => _source.Clone(); #endregion #region Object @@ -59,24 +63,41 @@ namespace DCFApixels.DragonECS return _source.ToString(); return "NULL"; } + public override int GetHashCode() => _source.GetHashCode(); + public override bool Equals(object obj) => obj is EcsGroup group && group == this; + public bool Equals(EcsReadonlyGroup other) => _source == other._source; #endregion #region Internal - internal void Release() - { - _source.World.ReleaseGroup(_source); - } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal EcsGroup GetGroupInternal() => _source; #endregion + + #region operators + public static bool operator ==(EcsReadonlyGroup a, EcsReadonlyGroup b) => a.Equals(b); + public static bool operator ==(EcsReadonlyGroup a, EcsGroup b) => a.Equals(b); + public static bool operator !=(EcsReadonlyGroup a, EcsReadonlyGroup b) => !a.Equals(b); + public static bool operator !=(EcsReadonlyGroup a, EcsGroup b) => !a.Equals(b); + #endregion + + #region Dispose/Release + public void Dispose() + { + _source.Dispose(); + } + public void Release() + { + _source.World.ReleaseGroup(_source); + } + #endregion } // не может содержать значение 0 // _delayedOps это int[] для отложенных операций, хранятся отложенные операции в виде int значения, если старший бит = 0 то это опреация добавленияб если = 1 то это операция вычитания // this collection can only store numbers greater than 0 - public unsafe class EcsGroup + public unsafe class EcsGroup : IDisposable, IEquatable { private const int DEALAYED_ADD = 0; private const int DEALAYED_REMOVE = int.MinValue; @@ -93,6 +114,8 @@ namespace DCFApixels.DragonECS private int _lockCount; + private bool _isReleazed = true; + #region Properties public IEcsWorld World => _source; public int Count @@ -115,16 +138,25 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new EcsReadonlyGroup(this); } + public bool IsReleazed + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _isReleazed; + } #endregion #region Constrcutors - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsGroup(IEcsWorld source, int denseCapacity = 64, int delayedOpsCapacity = 128) + public static EcsGroup New(IEcsWorld world) { - _source = source; + return world.GetGroupFromPool(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal EcsGroup(IEcsWorld world, int denseCapacity = 64, int delayedOpsCapacity = 128) + { + _source = world; _source.RegisterGroup(this); _dense = new int[denseCapacity]; - _sparse = new int[source.EntitesCapacity]; + _sparse = new int[world.EntitesCapacity]; _delayedOps = new delayedOp[delayedOpsCapacity]; @@ -132,31 +164,13 @@ namespace DCFApixels.DragonECS _delayedOpsCount = 0; _count = 0; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsGroup(EcsGroup copyFrom, int delayedOpsCapacity = 128) - { - _source = copyFrom._source; - _source.RegisterGroup(this); - _dense = new int[copyFrom._dense.Length]; - _sparse = new int[copyFrom._sparse.Length]; - - _delayedOps = new delayedOp[delayedOpsCapacity]; - - _lockCount = 0; - _delayedOpsCount = 0; - _count = 0; - - foreach (var item in copyFrom) - AggressiveAdd(item.id); - } #endregion #region Contains [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(int entityID) { - return _sparse[entityID] > 0; + return _sparse[entityID] > 0; } #endregion @@ -229,12 +243,7 @@ namespace DCFApixels.DragonECS } #endregion - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void OnWorldResize(int newSize) - { - Array.Resize(ref _sparse, newSize); - } - + #region Sort/Clear public void Sort() { int increment = 1; @@ -247,9 +256,20 @@ namespace DCFApixels.DragonECS } } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Clear() => _count = 0; + public void Clear() + { + _count = 0; + for (int i = 0; i < _dense.Length; i++) + _dense[i] = 0; + for (int i = 0; i < _sparse.Length; i++) + _sparse[i] = 0; + } + #endregion + + #region CopyFrom/Clone + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CopyFrom(EcsReadonlyGroup group) => CopyFrom(group.GetGroupInternal()); public void CopyFrom(EcsGroup group) { #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS @@ -259,29 +279,20 @@ namespace DCFApixels.DragonECS foreach (var item in group) AggressiveAdd(item.id); } - public void CopyFrom(EcsReadonlyGroup group) + public EcsGroup Clone() { -#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS - if (group.World != _source) throw new ArgumentException("groupFilter.World != World"); -#endif - Clear(); - foreach (var item in group) - AggressiveAdd(item.id); + EcsGroup result = _source.GetGroupFromPool(); + result.CopyFrom(this); + return result; } + #endregion #region Set operations /// as Union sets - public void AddGroup(EcsGroup group) - { -#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS - if (_source != group.World) throw new ArgumentException("World != groupFilter.World"); -#endif - foreach (var item in group) - if (!Contains(item.id)) - AggressiveAdd(item.id); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void UnionWith(EcsReadonlyGroup group) => UnionWith(group.GetGroupInternal()); /// as Union sets - public void AddGroup(EcsReadonlyGroup group) + public void UnionWith(EcsGroup group) { #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (_source != group.World) throw new ArgumentException("World != groupFilter.World"); @@ -290,8 +301,12 @@ namespace DCFApixels.DragonECS if (!Contains(item.id)) AggressiveAdd(item.id); } + /// as Except sets - public void RemoveGroup(EcsGroup group) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ExceptWith(EcsReadonlyGroup group) => ExceptWith(group.GetGroupInternal()); + /// as Except sets + public void ExceptWith(EcsGroup group) { #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (_source != group.World) throw new ArgumentException("World != groupFilter.World"); @@ -300,16 +315,10 @@ namespace DCFApixels.DragonECS if (group.Contains(item.id)) AggressiveRemove(item.id); } - /// as Except sets - public void RemoveGroup(EcsReadonlyGroup group) - { -#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS - if (_source != group.World) throw new ArgumentException("World != groupFilter.World"); -#endif - foreach (var item in group) - if (Contains(item.id)) - AggressiveRemove(item.id); - } + + /// as Intersect sets + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AndWith(EcsReadonlyGroup group) => AndWith(group.GetGroupInternal()); /// as Intersect sets public void AndWith(EcsGroup group) { @@ -320,33 +329,15 @@ namespace DCFApixels.DragonECS if (!group.Contains(item.id)) AggressiveRemove(item.id); } - /// as Intersect sets - public void AndWith(EcsReadonlyGroup group) - { -#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS - if (World != group.World) throw new ArgumentException("World != groupFilter.World"); -#endif - foreach (var item in this) - if (!group.Contains(item.id)) - AggressiveRemove(item.id); - } + + /// as Symmetric Except sets + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void XorWith(EcsReadonlyGroup group) => XorWith(group.GetGroupInternal()); /// as Symmetric Except sets public void XorWith(EcsGroup group) { #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (_source != group.World) throw new ArgumentException("World != groupFilter.World"); -#endif - foreach (var item in group) - if (Contains(item.id)) - AggressiveRemove(item.id); - else - AggressiveAdd(item.id); - } - /// as Symmetric Except sets - public void XorWith(EcsReadonlyGroup group) - { -#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS - if (_source != group.World) throw new ArgumentException("World != groupFilter.World"); #endif foreach (var item in group) if (Contains(item.id)) @@ -358,7 +349,8 @@ namespace DCFApixels.DragonECS #region Static Set operations /// as Except sets - public static EcsReadonlyGroup Remove(EcsGroup a, EcsGroup b) + /// new group from pool + public static EcsGroup Except(EcsGroup a, EcsGroup b) { #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (a._source != b._source) throw new ArgumentException("a.World != b.World"); @@ -368,10 +360,11 @@ namespace DCFApixels.DragonECS if (!b.Contains(item.id)) result.AggressiveAdd(item.id); a._source.ReleaseGroup(a); - return result.Readonly; + return result; } /// as Intersect sets - public static EcsReadonlyGroup And(EcsGroup a, EcsGroup b) + /// new group from pool + public static EcsGroup And(EcsGroup a, EcsGroup b) { #if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS if (a._source != b._source) throw new ArgumentException("a.World != b.World"); @@ -381,7 +374,21 @@ namespace DCFApixels.DragonECS if (b.Contains(item.id)) result.AggressiveAdd(item.id); a._source.ReleaseGroup(a); - return result.Readonly; + return result; + } + /// as Intersect sets + /// new group from pool + public static EcsGroup Union(EcsGroup a, EcsGroup b) + { +#if (DEBUG && !DISABLE_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS + if (a._source != b._source) throw new ArgumentException("a.World != b.World"); +#endif + EcsGroup result = a._source.GetGroupFromPool(); + foreach (var item in a) + result.AggressiveAdd(item.id); + foreach (var item in a) + result.Add(item.id); + return result; } #endregion @@ -450,6 +457,51 @@ namespace DCFApixels.DragonECS { return string.Join(", ", _dense.AsSpan(1, _count).ToArray()); } + public override bool Equals(object obj) => obj is EcsGroup group && Equals(group); + public bool Equals(EcsReadonlyGroup other) => Equals(other.GetGroupInternal()); + public bool Equals(EcsGroup other) + { + if (other.Count != Count) + return false; + foreach (var item in other) + if (!Contains(item.id)) + return false; + return true; + } + public override int GetHashCode() + { + int hash = 0; + foreach (var item in this) + hash ^= 1 << (item.id % 32); //реализация от балды, так как не нужен, но фишка в том что хеш не учитывает порядок сущьностей, что явлется правильным поведением. + return hash; + } + #endregion + + #region operators + public static bool operator ==(EcsGroup a, EcsGroup b) => a.Equals(b); + public static bool operator ==(EcsGroup a, EcsReadonlyGroup b) => a.Equals(b); + public static bool operator !=(EcsGroup a, EcsGroup b) => !a.Equals(b); + public static bool operator !=(EcsGroup a, EcsReadonlyGroup b) => !a.Equals(b); + #endregion + + #region OnWorldResize + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void OnWorldResize(int newSize) + { + Array.Resize(ref _sparse, newSize); + } + #endregion + + #region IDisposable/Release + public void Dispose() + { + Release(); + } + public void Release() + { + _isReleazed = true; + _source.ReleaseGroup(this); + } #endregion } diff --git a/src/EcsPool.cs b/src/EcsPool.cs index 8d7674d..682e784 100644 --- a/src/EcsPool.cs +++ b/src/EcsPool.cs @@ -10,7 +10,6 @@ namespace DCFApixels.DragonECS public Type ComponentType { get; } public int ComponentID { get; } public IEcsWorld World { get; } - public EcsReadonlyGroup Entities { get; } public int Count { get; } public int Capacity { get; } public bool Has(int entityID); @@ -34,7 +33,6 @@ namespace DCFApixels.DragonECS public Type ComponentType => typeof(NullComponent); public int ComponentID => -1; public IEcsWorld World => _source; - public EcsReadonlyGroup Entities => default; public int Count => 0; public int Capacity => 1; public void Del(int index) { } @@ -47,7 +45,6 @@ namespace DCFApixels.DragonECS } public abstract class EcsPool { - internal EcsGroup entities; public abstract bool Has(int entityID); internal abstract void OnWorldResize(int newSize); } @@ -66,7 +63,6 @@ namespace DCFApixels.DragonECS private IEcsComponentReset _componentResetHandler; private PoolRunnres _poolRunnres; #region Properites - public EcsReadonlyGroup Entities => entities.Readonly; public int Count => _itemsCount; public int Capacity => _items.Length; public IEcsWorld World => _source; @@ -77,7 +73,6 @@ namespace DCFApixels.DragonECS #region Constructors internal EcsPool(IEcsWorld source, int id, int capacity, PoolRunnres poolRunnres) { - entities = new EcsGroup(source); _source = source; _componentID = id; @@ -105,7 +100,6 @@ namespace DCFApixels.DragonECS ref int itemIndex = ref _mapping[entityID]; if (itemIndex <= 0) { - entities.Add(entityID); if (_recycledItemsCount > 0) { itemIndex = _recycledItems[--_recycledItemsCount]; @@ -147,8 +141,6 @@ namespace DCFApixels.DragonECS { // using (_delMark.Auto()) // { - entities.Remove(entityID); - if (_recycledItemsCount >= _recycledItems.Length) Array.Resize(ref _recycledItems, _recycledItems.Length << 1); _recycledItems[_recycledItemsCount++] = _mapping[entityID]; @@ -168,7 +160,7 @@ namespace DCFApixels.DragonECS #region Object public override bool Equals(object obj) => base.Equals(obj); - public override int GetHashCode() => _source.GetHashCode() + ~ComponentID; + public override int GetHashCode() => _source.GetHashCode() ^ ~ComponentID; #endregion #region Internal diff --git a/src/EcsQuery.cs b/src/EcsQuery.cs index c081e18..35c8143 100644 --- a/src/EcsQuery.cs +++ b/src/EcsQuery.cs @@ -16,46 +16,37 @@ namespace DCFApixels.DragonECS private ProfilerMarker _getEnumerator = new ProfilerMarker("EcsQuery.GetEnumerator"); + public EcsGroup.Enumerator GetEnumerator() { using (_getEnumerator.Auto()) { - // groupFilter.Clear(); - var pools = World.GetAllPools(); - // - // if (mask.Inc.Length > 0) - // { - // groupFilter.CopyFrom(pools[mask.Inc[0]].entities); - // for (int i = 1; i < mask.Inc.Length; i++) - // { - // groupFilter.AndWith(pools[mask.Inc[i]].entities); - // } - // } - // else - // { - // groupFilter.CopyFrom(World.Entities); - // } - // for (int i = 0; i < mask.Exc.Length; i++) - // { - // groupFilter.RemoveGroup(pools[mask.Exc[i]].entities); - // } - // - // groupFilter.Sort(); - // return groupFilter.GetEnumerator(); - // - EcsReadonlyGroup sum = World.Entities; - for (int i = 0; i < mask.Inc.Length; i++) + var pools = World.GetAllPools(); + + EcsReadonlyGroup all = World.Entities; + groupFilter.Clear(); + foreach (var e in all) { - sum = EcsGroup.And(sum.GetGroupInternal(), pools[mask.Inc[i]].entities); - // Debug.Log("inc " + sum.ToString()); + int entityID = e.id; + + for (int i = 0, iMax = mask.Inc.Length; i < iMax; i++) + { + if (!pools[mask.Inc[i]].Has(entityID)) + { + continue; + } + } + for (int i = 0, iMax = mask.Exc.Length; i < iMax; i++) + { + if (pools[mask.Exc[i]].Has(entityID)) + { + continue; + } + } + groupFilter.AggressiveAdd(entityID); } - for (int i = 0; i < mask.Exc.Length; i++) - { - sum = EcsGroup.Remove(sum.GetGroupInternal(), pools[mask.Exc[i]].entities); - // Debug.Log("exc " + sum.ToString()); - } - //sum.GetGroupInternal().Sort(); - return sum.GetEnumerator(); + groupFilter.Sort(); + return groupFilter.GetEnumerator(); } } protected virtual void Init(Builder b) { } @@ -85,7 +76,8 @@ namespace DCFApixels.DragonECS } builder.End(out newQuery.mask); - newQuery.groupFilter = new EcsGroup(world); + // newQuery.groupFilter = new EcsGroup(world); + newQuery.groupFilter = EcsGroup.New(world); return (TQuery)(object)newQuery; } @@ -96,7 +88,6 @@ namespace DCFApixels.DragonECS _exc = new List(4); } - #region Init query member methods public override inc Include() where TComponent : struct { _inc.Add(_world.GetComponentID()); @@ -111,18 +102,14 @@ namespace DCFApixels.DragonECS { return new opt(_world.GetPool()); } - #endregion private void End(out EcsQueryMask mask) { _inc.Sort(); _exc.Sort(); mask = new EcsQueryMask(_world.ArchetypeType, _inc.ToArray(), _exc.ToArray()); - _world = null; - _inc.Clear(); _inc = null; - _exc.Clear(); _exc = null; } } diff --git a/src/EcsTable.cs b/src/EcsTable.cs deleted file mode 100644 index 152beb7..0000000 --- a/src/EcsTable.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; - -namespace DCFApixels.DragonECS -{ - public class EcsTable - { - private IEcsPool[] _pools; - private EcsNullPool _nullPool; - - private short[] _gens; - private short[] _componentCounts; - - private int _entitiesCount; - - private List[] _filtersByIncludedComponents; - private List[] _filtersByExcludedComponents; - - private EcsQuery[] _queries; - - private List _groups; - - - #region Properties - public int Count => _entitiesCount; - public int Capacity => _gens.Length; - public ReadOnlySpan GetAllPools() => new ReadOnlySpan(_pools); - #endregion - - #region internal Add/Has/Remove - internal void Add(int entityID) - { - int entity; - if (_entitiesCount >= _gens.Length) - { - Array.Resize(ref _gens, _gens.Length << 1); - Array.Resize(ref _componentCounts, _componentCounts.Length << 1); - } - _gens[_entitiesCount++]++; - _componentCounts[_entitiesCount++] = 0; - - // if (_gens.Length <= entityID) - // { - // //TODO есть проблема что если передать слишком большой id такой алогоритм не сработает - // } - - - } - internal void Has(int entityID) - { - - } - internal void Remove(int entityID) - { - - } - #endregion - - //public int GetComponentID() => ; - } - - -} diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index 79b9c6d..99f65e1 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -33,7 +33,7 @@ namespace DCFApixels.DragonECS public abstract class EcsWorld { - internal static IEcsWorld[] Worlds = new IEcsWorld[8]; + public static IEcsWorld[] Worlds = new IEcsWorld[8]; private static IntDispenser _worldIdDispenser = new IntDispenser(0); public readonly short id; @@ -196,18 +196,12 @@ namespace DCFApixels.DragonECS } #endregion - #region IsMaskCompatible/IsMaskCompatibleWithout - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsMaskCompatible(int entityID) where TInc : struct, IInc - { - return IsMaskCompatible(EcsMaskMap.GetMask(), entityID); - } + #region IsMaskCompatible [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsMaskCompatible(int entityID) where TInc : struct, IInc where TExc : struct, IExc { return IsMaskCompatible(EcsMaskMap.GetMask(), entityID); } - public bool IsMaskCompatible(EcsComponentMask mask, int entityID) { #if (DEBUG && !DISABLE_DRAGONECS_DEBUG) || !DRAGONECS_NO_SANITIZE_CHECKS @@ -348,9 +342,7 @@ namespace DCFApixels.DragonECS EcsGroup IEcsWorld.GetGroupFromPool() { if (_pool.Count <= 0) - { return new EcsGroup(this); - } return _pool.Pop(); } void IEcsWorld.ReleaseGroup(EcsGroup group) diff --git a/src/Entities/ent.cs b/src/Entities/ent.cs index b4bee19..c5ddd38 100644 --- a/src/Entities/ent.cs +++ b/src/Entities/ent.cs @@ -1,5 +1,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using UnityEngine.Rendering; namespace DCFApixels.DragonECS { diff --git a/src/TestPool.cs b/src/TestPool.cs new file mode 100644 index 0000000..a34ae1a --- /dev/null +++ b/src/TestPool.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DCFApixels.DragonECS.Test +{ + public class TestWorld + { + public PoolToken RegisterPool() + { + return new PoolToken(1); + } + } + + public readonly struct PoolToken + { + internal readonly ushort id; + public PoolToken(ushort id) + { + this.id = id; + } + } + //реализовать query так чтоб на вход он получал какуюто коллекцию и заполнял ее. по итогу на выходе запроса юзер будет иметь просто список + //таким образом во первых он сам может решить как его прокрутить, через for или foreach или еще как. во вторых можно будет прикрутить поддержку nativearray от unity + + public class TestPool + { + private PoolToken _token; + public TestPool(TestWorld world) + { + _token = world.RegisterPool(); + } + } +}