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.

194 lines
9.2KB

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