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.

214 lines
9.6KB

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