Mirror of Svelto.ECS because we're a fan of it
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
9.5KB

  1. using System.Runtime.CompilerServices;
  2. using Svelto.Common;
  3. using Svelto.DataStructures;
  4. using Svelto.ECS.DataStructures;
  5. namespace Svelto.ECS
  6. {
  7. /// <summary>
  8. /// In order to complete this feature, I need to be able to detect if the entity pointed by the filter
  9. /// is still present.
  10. /// This feature should work only with groups where entityID cannot be chosen by the user, so that
  11. /// a real sparse set can be used like explained at: https://skypjack.github.io/2020-08-02-ecs-baf-part-9/
  12. /// For a sparse set to work, the index in the sparse list must coincide with the ID of the entity
  13. /// so that from the dense list (that holds unordered entity index), I can get to the sparse list index
  14. /// sparse[0] = position in the dense list of the entity 0
  15. /// dense[index] = entity ID but also index in the sparse list of the same entity ID
  16. /// </summary>
  17. public struct FilterGroup
  18. {
  19. internal FilterGroup(ExclusiveGroupStruct exclusiveGroupStruct, int ID)
  20. {
  21. _denseListOfIndicesToEntityComponentArray =
  22. new NativeDynamicArrayCast<uint>(NativeDynamicArray.Alloc<uint>(Allocator.Persistent));
  23. //from the index, find the entityID
  24. _reverseEIDs = new NativeDynamicArrayCast<uint>(NativeDynamicArray.Alloc<uint>(Allocator.Persistent));
  25. //from the entityID, find the index
  26. _indexOfEntityInDenseList = new SharedSveltoDictionaryNative<uint, uint>(0);
  27. _exclusiveGroupStruct = exclusiveGroupStruct;
  28. _ID = ID;
  29. }
  30. /// <summary>
  31. /// Todo: how to detect if the indices are still pointing to valid entities?
  32. /// </summary>
  33. public FilteredIndices filteredIndices => new FilteredIndices(_denseListOfIndicesToEntityComponentArray);
  34. public bool Add<N>(uint entityID, N mapper) where N:IEGIDMapper
  35. {
  36. #if DEBUG && !PROFILE_SVELTO
  37. if (mapper.Exists(entityID) == false)
  38. throw new ECSException(
  39. $"trying adding an entity {entityID} to filter {mapper.entityType} - {_ID} with group {mapper.groupID}, but entity is not found! ");
  40. #endif
  41. return InternalAdd(entityID, mapper.GetIndex(entityID));
  42. }
  43. public void Remove(uint entityID)
  44. {
  45. #if DEBUG && !PROFILE_SVELTO
  46. if (_denseListOfIndicesToEntityComponentArray.isValid == false)
  47. throw new ECSException($"invalid Filter");
  48. if (_indexOfEntityInDenseList.ContainsKey(entityID) == false)
  49. throw new ECSException(
  50. $"trying to remove a not existing entity {new EGID(entityID, _exclusiveGroupStruct)} from filter");
  51. #endif
  52. InternalRemove(entityID);
  53. }
  54. public bool Exists(uint entityID)
  55. {
  56. #if DEBUG && !PROFILE_SVELTO
  57. if (_denseListOfIndicesToEntityComponentArray.isValid == false)
  58. throw new ECSException($"invalid Filter");
  59. #endif
  60. return _indexOfEntityInDenseList.ContainsKey(entityID);
  61. }
  62. public bool TryRemove(uint entityID)
  63. {
  64. #if DEBUG && !PROFILE_SVELTO
  65. if (_denseListOfIndicesToEntityComponentArray.isValid == false)
  66. throw new ECSException($"invalid Filter");
  67. #endif
  68. if (_indexOfEntityInDenseList.ContainsKey(entityID) == false)
  69. return false;
  70. InternalRemove(entityID);
  71. return true;
  72. }
  73. /// <summary>
  74. /// Filters were initially designed to be used for tagging operations within submissions of entities.
  75. /// They were designed as a fast tagging mechanism to be used within the submission frame. However I then
  76. /// extended it, but the extension includes a drawback:
  77. ///If filters are not in sync with the operations of remove and swap, filters may end up pointing to
  78. ///invalid indices. I need to put in place a way to be able to recognised an invalid filter.
  79. ///This is currently a disadvantage of the filters. The filters are not updated by the framework
  80. ///but they must be updated by the user.
  81. ///When to use this method: Add and Removed should be used to add and remove entities in the filters. This is
  82. /// valid as long as no structural changes happen in the group of entities involved.
  83. /// IF structural changes happen, the indices stored in the filters won't be valid anymore as they will possibly
  84. /// point to entities that were not the original ones. On structural changes
  85. /// (specifically entities swapped or removed)
  86. /// the filters must then be rebuilt. It would be too slow to add this in the standard flow of Svelto in
  87. /// the current state, so calling this method is a user responsibility.
  88. /// </summary>
  89. public void RebuildIndicesOnStructuralChange<N>(N mapper) where N:IEGIDMapper
  90. {
  91. #if DEBUG && !PROFILE_SVELTO
  92. if (_denseListOfIndicesToEntityComponentArray.isValid == false)
  93. throw new ECSException($"invalid Filter");
  94. #endif
  95. _denseListOfIndicesToEntityComponentArray.Clear();
  96. _reverseEIDs.Clear();
  97. foreach (var value in _indexOfEntityInDenseList)
  98. if (mapper.FindIndex(value.Key, out var indexOfEntityInBufferComponent) == true)
  99. {
  100. _denseListOfIndicesToEntityComponentArray.Add(indexOfEntityInBufferComponent);
  101. var lastIndex = (uint) (_denseListOfIndicesToEntityComponentArray.Count() - 1);
  102. _reverseEIDs.AddAt(lastIndex) = value.Key;
  103. }
  104. _indexOfEntityInDenseList.Clear();
  105. for (uint i = 0; i < _reverseEIDs.Count(); i++)
  106. _indexOfEntityInDenseList[_reverseEIDs[i]] = i;
  107. }
  108. public void Clear()
  109. {
  110. #if DEBUG && !PROFILE_SVELTO
  111. if (_denseListOfIndicesToEntityComponentArray.isValid == false)
  112. throw new ECSException($"invalid Filter");
  113. #endif
  114. _indexOfEntityInDenseList.FastClear();
  115. _reverseEIDs.Clear();
  116. _denseListOfIndicesToEntityComponentArray.Clear();
  117. }
  118. internal void Dispose()
  119. {
  120. #if DEBUG && !PROFILE_SVELTO
  121. if (_denseListOfIndicesToEntityComponentArray.isValid == false)
  122. throw new ECSException($"invalid Filter");
  123. #endif
  124. _denseListOfIndicesToEntityComponentArray.Dispose();
  125. _indexOfEntityInDenseList.Dispose();
  126. _reverseEIDs.Dispose();
  127. }
  128. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  129. internal bool InternalAdd(uint entityID, uint indexOfEntityInBufferComponent)
  130. {
  131. #if DEBUG && !PROFILE_SVELTO
  132. if (_denseListOfIndicesToEntityComponentArray.isValid == false)
  133. throw new ECSException($"using an invalid filter");
  134. #endif
  135. if (_indexOfEntityInDenseList.ContainsKey(entityID) == true)
  136. return false;
  137. //add the index in the list of filtered indices
  138. _denseListOfIndicesToEntityComponentArray.Add(indexOfEntityInBufferComponent);
  139. //inverse map: need to get the entityID from the index. This wouldn't be needed with a real sparseset
  140. var lastIndex = (uint) (_denseListOfIndicesToEntityComponentArray.Count() - 1);
  141. _reverseEIDs.AddAt(lastIndex) = entityID;
  142. //remember the entities indices. This is needed to remove entities from the filter
  143. _indexOfEntityInDenseList.Add(entityID, lastIndex);
  144. return true;
  145. }
  146. void InternalRemove(uint entityID)
  147. {
  148. var count = (uint) _denseListOfIndicesToEntityComponentArray.Count();
  149. if (count > 0)
  150. {
  151. if (count > 1)
  152. {
  153. //get the index in the filter array of the entity to delete
  154. var indexInDenseListFromEGID = _indexOfEntityInDenseList[entityID];
  155. //get the entityID of the last entity in the filter array
  156. uint entityIDToMove = _reverseEIDs[count - 1];
  157. //the last index of the last entity is updated to the slot of the deleted entity
  158. if (entityIDToMove != entityID)
  159. {
  160. _indexOfEntityInDenseList[entityIDToMove] = indexInDenseListFromEGID;
  161. //the reverseEGID is updated accordingly
  162. _reverseEIDs[indexInDenseListFromEGID] = entityIDToMove;
  163. }
  164. //
  165. _reverseEIDs.UnorderedRemoveAt(count - 1);
  166. //finally remove the deleted entity from the filters array
  167. _denseListOfIndicesToEntityComponentArray.UnorderedRemoveAt(indexInDenseListFromEGID);
  168. //remove the entity to delete from the tracked Entity
  169. _indexOfEntityInDenseList.Remove(entityID);
  170. }
  171. else
  172. {
  173. _indexOfEntityInDenseList.FastClear();
  174. _reverseEIDs.Clear();
  175. _denseListOfIndicesToEntityComponentArray.Clear();
  176. }
  177. }
  178. }
  179. NativeDynamicArrayCast<uint> _denseListOfIndicesToEntityComponentArray;
  180. NativeDynamicArrayCast<uint> _reverseEIDs; //forced to use this because it's not a real sparse set
  181. SharedSveltoDictionaryNative<uint, uint> _indexOfEntityInDenseList;
  182. internal readonly ExclusiveGroupStruct _exclusiveGroupStruct;
  183. internal readonly int _ID;
  184. }
  185. }