EcsFilter.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. // ----------------------------------------------------------------------------
  2. // The MIT License
  3. // Simple Entity Component System framework https://github.com/Leopotam/ecs
  4. // Copyright (c) 2017-2020 Leopotam <leopotam@gmail.com>
  5. // ----------------------------------------------------------------------------
  6. using System;
  7. using System.Runtime.CompilerServices;
  8. // ReSharper disable InconsistentNaming
  9. // ReSharper disable ClassNeverInstantiated.Global
  10. namespace Leopotam.Ecs {
  11. /// <summary>
  12. /// Common interface for all filter listeners.
  13. /// </summary>
  14. public interface IEcsFilterListener {
  15. void OnEntityAdded (in EcsEntity entity);
  16. void OnEntityRemoved (in EcsEntity entity);
  17. }
  18. /// <summary>
  19. /// Container for filtered entities based on specified constraints.
  20. /// </summary>
  21. #if ENABLE_IL2CPP
  22. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
  23. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
  24. #endif
  25. #if UNITY_2019_1_OR_NEWER
  26. [UnityEngine.Scripting.Preserve]
  27. #endif
  28. public abstract class EcsFilter {
  29. public EcsEntity[] Entities = new EcsEntity[EcsHelpers.FilterEntitiesSize];
  30. protected int EntitiesCount;
  31. int _lockCount;
  32. DelayedOp[] _delayedOps = new DelayedOp[EcsHelpers.FilterEntitiesSize];
  33. int _delayedOpsCount;
  34. // ReSharper disable MemberCanBePrivate.Global
  35. protected IEcsFilterListener[] Listeners = new IEcsFilterListener[4];
  36. protected int ListenersCount;
  37. // ReSharper restore MemberCanBePrivate.Global
  38. protected internal int[] IncludedTypeIndices;
  39. protected internal int[] ExcludedTypeIndices;
  40. public Type[] IncludedTypes;
  41. public Type[] ExcludedTypes;
  42. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  43. public Enumerator GetEnumerator () {
  44. return new Enumerator (this);
  45. }
  46. /// <summary>
  47. /// Gets entities count.
  48. /// </summary>
  49. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  50. public int GetEntitiesCount () {
  51. return EntitiesCount;
  52. }
  53. /// <summary>
  54. /// Is filter not contains entities.
  55. /// </summary>
  56. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  57. public bool IsEmpty () {
  58. return EntitiesCount == 0;
  59. }
  60. /// <summary>
  61. /// Subscribes listener to filter events.
  62. /// </summary>
  63. /// <param name="listener">Listener.</param>
  64. public void AddListener (IEcsFilterListener listener) {
  65. #if DEBUG
  66. for (int i = 0, iMax = ListenersCount; i < iMax; i++) {
  67. if (Listeners[i] == listener) {
  68. throw new Exception ("Listener already subscribed.");
  69. }
  70. }
  71. #endif
  72. if (Listeners.Length == ListenersCount) {
  73. Array.Resize (ref Listeners, ListenersCount << 1);
  74. }
  75. Listeners[ListenersCount++] = listener;
  76. }
  77. // ReSharper disable once CommentTypo
  78. /// <summary>
  79. /// Unsubscribes listener from filter events.
  80. /// </summary>
  81. /// <param name="listener">Listener.</param>
  82. public void RemoveListener (IEcsFilterListener listener) {
  83. for (int i = 0, iMax = ListenersCount; i < iMax; i++) {
  84. if (Listeners[i] == listener) {
  85. ListenersCount--;
  86. // cant fill gap with last element due listeners order is important.
  87. Array.Copy (Listeners, i + 1, Listeners, i, ListenersCount - i);
  88. break;
  89. }
  90. }
  91. }
  92. /// <summary>
  93. /// Is filter compatible with components on entity with optional added / removed component.
  94. /// </summary>
  95. /// <param name="entityData">Entity data.</param>
  96. /// <param name="addedRemovedTypeIndex">Optional added (greater 0) or removed (less 0) component. Will be ignored if zero.</param>
  97. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  98. internal bool IsCompatible (in EcsWorld.EcsEntityData entityData, int addedRemovedTypeIndex) {
  99. var incIdx = IncludedTypeIndices.Length - 1;
  100. for (; incIdx >= 0; incIdx--) {
  101. var typeIdx = IncludedTypeIndices[incIdx];
  102. var idx = entityData.ComponentsCountX2 - 2;
  103. for (; idx >= 0; idx -= 2) {
  104. var typeIdx2 = entityData.Components[idx];
  105. if (typeIdx2 == -addedRemovedTypeIndex) {
  106. continue;
  107. }
  108. if (typeIdx2 == addedRemovedTypeIndex || typeIdx2 == typeIdx) {
  109. break;
  110. }
  111. }
  112. // not found.
  113. if (idx == -2) {
  114. break;
  115. }
  116. }
  117. // one of required component not found.
  118. if (incIdx != -1) {
  119. return false;
  120. }
  121. // check for excluded components.
  122. if (ExcludedTypeIndices != null) {
  123. for (var excIdx = 0; excIdx < ExcludedTypeIndices.Length; excIdx++) {
  124. var typeIdx = ExcludedTypeIndices[excIdx];
  125. for (var idx = entityData.ComponentsCountX2 - 2; idx >= 0; idx -= 2) {
  126. var typeIdx2 = entityData.Components[idx];
  127. if (typeIdx2 == -addedRemovedTypeIndex) {
  128. continue;
  129. }
  130. if (typeIdx2 == addedRemovedTypeIndex || typeIdx2 == typeIdx) {
  131. return false;
  132. }
  133. }
  134. }
  135. }
  136. return true;
  137. }
  138. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  139. protected bool AddDelayedOp (bool isAdd, in EcsEntity entity) {
  140. if (_lockCount <= 0) {
  141. return false;
  142. }
  143. if (_delayedOps.Length == _delayedOpsCount) {
  144. Array.Resize (ref _delayedOps, _delayedOpsCount << 1);
  145. }
  146. ref var op = ref _delayedOps[_delayedOpsCount++];
  147. op.IsAdd = isAdd;
  148. op.Entity = entity;
  149. return true;
  150. }
  151. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  152. protected void ProcessListeners (bool isAdd, in EcsEntity entity) {
  153. if (isAdd) {
  154. for (int i = 0, iMax = ListenersCount; i < iMax; i++) {
  155. Listeners[i].OnEntityAdded (entity);
  156. }
  157. } else {
  158. for (int i = 0, iMax = ListenersCount; i < iMax; i++) {
  159. Listeners[i].OnEntityRemoved (entity);
  160. }
  161. }
  162. }
  163. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  164. void Lock () {
  165. _lockCount++;
  166. }
  167. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  168. void Unlock () {
  169. #if DEBUG
  170. if (_lockCount <= 0) {
  171. throw new Exception ($"Invalid lock-unlock balance for \"{GetType ().Name}\".");
  172. }
  173. #endif
  174. _lockCount--;
  175. if (_lockCount == 0 && _delayedOpsCount > 0) {
  176. // process delayed operations.
  177. for (int i = 0, iMax = _delayedOpsCount; i < iMax; i++) {
  178. ref var op = ref _delayedOps[i];
  179. if (op.IsAdd) {
  180. OnAddEntity (op.Entity);
  181. } else {
  182. OnRemoveEntity (op.Entity);
  183. }
  184. }
  185. _delayedOpsCount = 0;
  186. }
  187. }
  188. #if DEBUG
  189. /// <summary>
  190. /// For debug purposes. Check filters equality by included / excluded components.
  191. /// </summary>
  192. /// <param name="filter">Filter to compare.</param>
  193. internal bool AreComponentsSame (EcsFilter filter) {
  194. if (IncludedTypeIndices.Length != filter.IncludedTypeIndices.Length) {
  195. return false;
  196. }
  197. for (var i = 0; i < IncludedTypeIndices.Length; i++) {
  198. if (Array.IndexOf (filter.IncludedTypeIndices, IncludedTypeIndices[i]) == -1) {
  199. return false;
  200. }
  201. }
  202. if ((ExcludedTypeIndices == null && filter.ExcludedTypeIndices != null) ||
  203. (ExcludedTypeIndices != null && filter.ExcludedTypeIndices == null)) {
  204. return false;
  205. }
  206. if (ExcludedTypeIndices != null) {
  207. if (filter.ExcludedTypeIndices == null || ExcludedTypeIndices.Length != filter.ExcludedTypeIndices.Length) {
  208. return false;
  209. }
  210. for (var i = 0; i < ExcludedTypeIndices.Length; i++) {
  211. if (Array.IndexOf (filter.ExcludedTypeIndices, ExcludedTypeIndices[i]) == -1) {
  212. return false;
  213. }
  214. }
  215. }
  216. return true;
  217. }
  218. #endif
  219. /// <summary>
  220. /// Event for adding compatible entity to filter.
  221. /// Warning: Don't call manually!
  222. /// </summary>
  223. /// <param name="entity">Entity.</param>
  224. public abstract void OnAddEntity (in EcsEntity entity);
  225. /// <summary>
  226. /// Event for removing non-compatible entity to filter.
  227. /// Warning: Don't call manually!
  228. /// </summary>
  229. /// <param name="entity">Entity.</param>
  230. public abstract void OnRemoveEntity (in EcsEntity entity);
  231. public struct Enumerator : IDisposable {
  232. readonly EcsFilter _filter;
  233. readonly int _count;
  234. int _idx;
  235. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  236. internal Enumerator (EcsFilter filter) {
  237. _filter = filter;
  238. _count = _filter.GetEntitiesCount ();
  239. _idx = -1;
  240. _filter.Lock ();
  241. }
  242. public int Current {
  243. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  244. get => _idx;
  245. }
  246. #if ENABLE_IL2CPP
  247. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
  248. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
  249. #endif
  250. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  251. public void Dispose () {
  252. _filter.Unlock ();
  253. }
  254. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  255. public bool MoveNext () {
  256. return ++_idx < _count;
  257. }
  258. }
  259. struct DelayedOp {
  260. public bool IsAdd;
  261. public EcsEntity Entity;
  262. }
  263. }
  264. #if ENABLE_IL2CPP
  265. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
  266. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
  267. #endif
  268. #if UNITY_2019_1_OR_NEWER
  269. [UnityEngine.Scripting.Preserve]
  270. #endif
  271. public class EcsFilter<Inc1> : EcsFilter where Inc1 : class {
  272. public Inc1[] Get1;
  273. readonly bool _allow1;
  274. protected EcsFilter () {
  275. _allow1 = !EcsComponentType<Inc1>.IsIgnoreInFilter;
  276. Get1 = _allow1 ? new Inc1[EcsHelpers.FilterEntitiesSize] : null;
  277. IncludedTypeIndices = new[] { EcsComponentType<Inc1>.TypeIndex };
  278. IncludedTypes = new[] { EcsComponentType<Inc1>.Type };
  279. }
  280. /// <summary>
  281. /// Event for adding compatible entity to filter.
  282. /// Warning: Don't call manually!
  283. /// </summary>
  284. /// <param name="entity">Entity.</param>
  285. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  286. public override void OnAddEntity (in EcsEntity entity) {
  287. if (AddDelayedOp (true, entity)) { return; }
  288. if (Entities.Length == EntitiesCount) {
  289. Array.Resize (ref Entities, EntitiesCount << 1);
  290. if (_allow1) { Array.Resize (ref Get1, EntitiesCount << 1); }
  291. }
  292. // inlined and optimized World.GetComponent() call.
  293. ref var entityData = ref entity.Owner.GetEntityData (entity);
  294. var allow1 = _allow1;
  295. for (int i = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2) {
  296. var typeIdx = entityData.Components[i];
  297. var itemIdx = entityData.Components[i + 1];
  298. if (allow1 && typeIdx == EcsComponentType<Inc1>.TypeIndex) {
  299. Get1[EntitiesCount] = (Inc1) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
  300. allow1 = false;
  301. }
  302. }
  303. Entities[EntitiesCount++] = entity;
  304. ProcessListeners (true, entity);
  305. }
  306. /// <summary>
  307. /// Event for removing non-compatible entity to filter.
  308. /// Warning: Don't call manually!
  309. /// </summary>
  310. /// <param name="entity">Entity.</param>
  311. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  312. public override void OnRemoveEntity (in EcsEntity entity) {
  313. if (AddDelayedOp (false, entity)) { return; }
  314. for (int i = 0, iMax = EntitiesCount; i < iMax; i++) {
  315. if (Entities[i] == entity) {
  316. EntitiesCount--;
  317. if (i < EntitiesCount) {
  318. Entities[i] = Entities[EntitiesCount];
  319. if (_allow1) { Get1[i] = Get1[EntitiesCount]; }
  320. }
  321. ProcessListeners (false, entity);
  322. break;
  323. }
  324. }
  325. }
  326. public class Exclude<Exc1> : EcsFilter<Inc1> where Exc1 : class {
  327. protected Exclude () {
  328. ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex };
  329. ExcludedTypes = new[] { EcsComponentType<Exc1>.Type };
  330. }
  331. }
  332. public class Exclude<Exc1, Exc2> : EcsFilter<Inc1> where Exc1 : class where Exc2 : class {
  333. protected Exclude () {
  334. ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex, EcsComponentType<Exc2>.TypeIndex };
  335. ExcludedTypes = new[] { EcsComponentType<Exc1>.Type, EcsComponentType<Exc2>.Type };
  336. }
  337. }
  338. }
  339. #if ENABLE_IL2CPP
  340. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
  341. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
  342. #endif
  343. #if UNITY_2019_1_OR_NEWER
  344. [UnityEngine.Scripting.Preserve]
  345. #endif
  346. public class EcsFilter<Inc1, Inc2> : EcsFilter where Inc1 : class where Inc2 : class {
  347. public Inc1[] Get1;
  348. public Inc2[] Get2;
  349. readonly bool _allow1;
  350. readonly bool _allow2;
  351. protected EcsFilter () {
  352. _allow1 = !EcsComponentType<Inc1>.IsIgnoreInFilter;
  353. _allow2 = !EcsComponentType<Inc2>.IsIgnoreInFilter;
  354. Get1 = _allow1 ? new Inc1[EcsHelpers.FilterEntitiesSize] : null;
  355. Get2 = _allow2 ? new Inc2[EcsHelpers.FilterEntitiesSize] : null;
  356. IncludedTypeIndices = new[] { EcsComponentType<Inc1>.TypeIndex, EcsComponentType<Inc2>.TypeIndex };
  357. IncludedTypes = new[] { EcsComponentType<Inc1>.Type, EcsComponentType<Inc2>.Type };
  358. }
  359. /// <summary>
  360. /// Event for adding compatible entity to filter.
  361. /// Warning: Don't call manually!
  362. /// </summary>
  363. /// <param name="entity">Entity.</param>
  364. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  365. public override void OnAddEntity (in EcsEntity entity) {
  366. if (AddDelayedOp (true, entity)) { return; }
  367. if (Entities.Length == EntitiesCount) {
  368. Array.Resize (ref Entities, EntitiesCount << 1);
  369. if (_allow1) { Array.Resize (ref Get1, EntitiesCount << 1); }
  370. if (_allow2) { Array.Resize (ref Get2, EntitiesCount << 1); }
  371. }
  372. // inlined and optimized World.GetComponent() call.
  373. ref var entityData = ref entity.Owner.GetEntityData (entity);
  374. var allow1 = _allow1;
  375. var allow2 = _allow2;
  376. for (int i = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2) {
  377. var typeIdx = entityData.Components[i];
  378. var itemIdx = entityData.Components[i + 1];
  379. if (allow1 && typeIdx == EcsComponentType<Inc1>.TypeIndex) {
  380. Get1[EntitiesCount] = (Inc1) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
  381. allow1 = false;
  382. }
  383. if (allow2 && typeIdx == EcsComponentType<Inc2>.TypeIndex) {
  384. Get2[EntitiesCount] = (Inc2) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
  385. allow2 = false;
  386. }
  387. }
  388. Entities[EntitiesCount++] = entity;
  389. ProcessListeners (true, entity);
  390. }
  391. /// <summary>
  392. /// Event for removing non-compatible entity to filter.
  393. /// Warning: Don't call manually!
  394. /// </summary>
  395. /// <param name="entity">Entity.</param>
  396. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  397. public override void OnRemoveEntity (in EcsEntity entity) {
  398. if (AddDelayedOp (false, entity)) { return; }
  399. for (int i = 0, iMax = EntitiesCount; i < iMax; i++) {
  400. if (Entities[i] == entity) {
  401. EntitiesCount--;
  402. if (i < EntitiesCount) {
  403. Entities[i] = Entities[EntitiesCount];
  404. if (_allow1) { Get1[i] = Get1[EntitiesCount]; }
  405. if (_allow2) { Get2[i] = Get2[EntitiesCount]; }
  406. }
  407. ProcessListeners (false, entity);
  408. break;
  409. }
  410. }
  411. }
  412. public class Exclude<Exc1> : EcsFilter<Inc1, Inc2> where Exc1 : class {
  413. protected Exclude () {
  414. ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex };
  415. ExcludedTypes = new[] { EcsComponentType<Exc1>.Type };
  416. }
  417. }
  418. public class Exclude<Exc1, Exc2> : EcsFilter<Inc1, Inc2> where Exc1 : class where Exc2 : class {
  419. protected Exclude () {
  420. ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex, EcsComponentType<Exc2>.TypeIndex };
  421. ExcludedTypes = new[] { EcsComponentType<Exc1>.Type, EcsComponentType<Exc2>.Type };
  422. }
  423. }
  424. }
  425. #if ENABLE_IL2CPP
  426. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
  427. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
  428. #endif
  429. #if UNITY_2019_1_OR_NEWER
  430. [UnityEngine.Scripting.Preserve]
  431. #endif
  432. public class EcsFilter<Inc1, Inc2, Inc3> : EcsFilter where Inc1 : class where Inc2 : class where Inc3 : class {
  433. // ReSharper disable MemberCanBePrivate.Global
  434. public Inc1[] Get1;
  435. public Inc2[] Get2;
  436. public Inc3[] Get3;
  437. // ReSharper restore MemberCanBePrivate.Global
  438. readonly bool _allow1;
  439. readonly bool _allow2;
  440. readonly bool _allow3;
  441. protected EcsFilter () {
  442. _allow1 = !EcsComponentType<Inc1>.IsIgnoreInFilter;
  443. _allow2 = !EcsComponentType<Inc2>.IsIgnoreInFilter;
  444. _allow3 = !EcsComponentType<Inc3>.IsIgnoreInFilter;
  445. Get1 = _allow1 ? new Inc1[EcsHelpers.FilterEntitiesSize] : null;
  446. Get2 = _allow2 ? new Inc2[EcsHelpers.FilterEntitiesSize] : null;
  447. Get3 = _allow3 ? new Inc3[EcsHelpers.FilterEntitiesSize] : null;
  448. IncludedTypeIndices = new[] { EcsComponentType<Inc1>.TypeIndex, EcsComponentType<Inc2>.TypeIndex, EcsComponentType<Inc3>.TypeIndex };
  449. IncludedTypes = new[] { EcsComponentType<Inc1>.Type, EcsComponentType<Inc2>.Type, EcsComponentType<Inc3>.Type };
  450. }
  451. /// <summary>
  452. /// Event for adding compatible entity to filter.
  453. /// Warning: Don't call manually!
  454. /// </summary>
  455. /// <param name="entity">Entity.</param>
  456. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  457. public override void OnAddEntity (in EcsEntity entity) {
  458. if (AddDelayedOp (true, entity)) { return; }
  459. if (Entities.Length == EntitiesCount) {
  460. Array.Resize (ref Entities, EntitiesCount << 1);
  461. if (_allow1) { Array.Resize (ref Get1, EntitiesCount << 1); }
  462. if (_allow2) { Array.Resize (ref Get2, EntitiesCount << 1); }
  463. if (_allow3) { Array.Resize (ref Get3, EntitiesCount << 1); }
  464. }
  465. // inlined and optimized World.GetComponent() call.
  466. ref var entityData = ref entity.Owner.GetEntityData (entity);
  467. var allow1 = _allow1;
  468. var allow2 = _allow2;
  469. var allow3 = _allow3;
  470. for (int i = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2) {
  471. var typeIdx = entityData.Components[i];
  472. var itemIdx = entityData.Components[i + 1];
  473. if (allow1 && typeIdx == EcsComponentType<Inc1>.TypeIndex) {
  474. Get1[EntitiesCount] = (Inc1) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
  475. allow1 = false;
  476. }
  477. if (allow2 && typeIdx == EcsComponentType<Inc2>.TypeIndex) {
  478. Get2[EntitiesCount] = (Inc2) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
  479. allow2 = false;
  480. }
  481. if (allow3 && typeIdx == EcsComponentType<Inc3>.TypeIndex) {
  482. Get3[EntitiesCount] = (Inc3) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
  483. allow3 = false;
  484. }
  485. }
  486. Entities[EntitiesCount++] = entity;
  487. ProcessListeners (true, entity);
  488. }
  489. /// <summary>
  490. /// Event for removing non-compatible entity to filter.
  491. /// Warning: Don't call manually!
  492. /// </summary>
  493. /// <param name="entity">Entity.</param>
  494. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  495. public override void OnRemoveEntity (in EcsEntity entity) {
  496. if (AddDelayedOp (false, entity)) { return; }
  497. for (int i = 0, iMax = EntitiesCount; i < iMax; i++) {
  498. if (Entities[i] == entity) {
  499. EntitiesCount--;
  500. if (i < EntitiesCount) {
  501. Entities[i] = Entities[EntitiesCount];
  502. if (_allow1) { Get1[i] = Get1[EntitiesCount]; }
  503. if (_allow2) { Get2[i] = Get2[EntitiesCount]; }
  504. if (_allow3) { Get3[i] = Get3[EntitiesCount]; }
  505. }
  506. ProcessListeners (false, entity);
  507. break;
  508. }
  509. }
  510. }
  511. public class Exclude<Exc1> : EcsFilter<Inc1, Inc2, Inc3> where Exc1 : class {
  512. protected Exclude () {
  513. ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex };
  514. ExcludedTypes = new[] { EcsComponentType<Exc1>.Type };
  515. }
  516. }
  517. public class Exclude<Exc1, Exc2> : EcsFilter<Inc1, Inc2, Inc3> where Exc1 : class where Exc2 : class {
  518. protected Exclude () {
  519. ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex, EcsComponentType<Exc2>.TypeIndex };
  520. ExcludedTypes = new[] { EcsComponentType<Exc1>.Type, EcsComponentType<Exc2>.Type };
  521. }
  522. }
  523. }
  524. #if ENABLE_IL2CPP
  525. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
  526. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
  527. #endif
  528. #if UNITY_2019_1_OR_NEWER
  529. [UnityEngine.Scripting.Preserve]
  530. #endif
  531. public class EcsFilter<Inc1, Inc2, Inc3, Inc4> : EcsFilter where Inc1 : class where Inc2 : class where Inc3 : class where Inc4 : class {
  532. // ReSharper disable MemberCanBePrivate.Global
  533. public Inc1[] Get1;
  534. public Inc2[] Get2;
  535. public Inc3[] Get3;
  536. public Inc4[] Get4;
  537. // ReSharper restore MemberCanBePrivate.Global
  538. readonly bool _allow1;
  539. readonly bool _allow2;
  540. readonly bool _allow3;
  541. readonly bool _allow4;
  542. protected EcsFilter () {
  543. _allow1 = !EcsComponentType<Inc1>.IsIgnoreInFilter;
  544. _allow2 = !EcsComponentType<Inc2>.IsIgnoreInFilter;
  545. _allow3 = !EcsComponentType<Inc3>.IsIgnoreInFilter;
  546. _allow4 = !EcsComponentType<Inc4>.IsIgnoreInFilter;
  547. Get1 = _allow1 ? new Inc1[EcsHelpers.FilterEntitiesSize] : null;
  548. Get2 = _allow2 ? new Inc2[EcsHelpers.FilterEntitiesSize] : null;
  549. Get3 = _allow3 ? new Inc3[EcsHelpers.FilterEntitiesSize] : null;
  550. Get4 = _allow4 ? new Inc4[EcsHelpers.FilterEntitiesSize] : null;
  551. IncludedTypeIndices = new[] {
  552. EcsComponentType<Inc1>.TypeIndex,
  553. EcsComponentType<Inc2>.TypeIndex,
  554. EcsComponentType<Inc3>.TypeIndex,
  555. EcsComponentType<Inc4>.TypeIndex
  556. };
  557. IncludedTypes = new[] {
  558. EcsComponentType<Inc1>.Type,
  559. EcsComponentType<Inc2>.Type,
  560. EcsComponentType<Inc3>.Type,
  561. EcsComponentType<Inc4>.Type
  562. };
  563. }
  564. /// <summary>
  565. /// Event for adding compatible entity to filter.
  566. /// Warning: Don't call manually!
  567. /// </summary>
  568. /// <param name="entity">Entity.</param>
  569. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  570. public override void OnAddEntity (in EcsEntity entity) {
  571. if (AddDelayedOp (true, entity)) { return; }
  572. if (Entities.Length == EntitiesCount) {
  573. Array.Resize (ref Entities, EntitiesCount << 1);
  574. if (_allow1) { Array.Resize (ref Get1, EntitiesCount << 1); }
  575. if (_allow2) { Array.Resize (ref Get2, EntitiesCount << 1); }
  576. if (_allow3) { Array.Resize (ref Get3, EntitiesCount << 1); }
  577. if (_allow4) { Array.Resize (ref Get4, EntitiesCount << 1); }
  578. }
  579. // inlined and optimized World.GetComponent() call.
  580. ref var entityData = ref entity.Owner.GetEntityData (entity);
  581. var allow1 = _allow1;
  582. var allow2 = _allow2;
  583. var allow3 = _allow3;
  584. var allow4 = _allow4;
  585. for (int i = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2) {
  586. var typeIdx = entityData.Components[i];
  587. var itemIdx = entityData.Components[i + 1];
  588. if (allow1 && typeIdx == EcsComponentType<Inc1>.TypeIndex) {
  589. Get1[EntitiesCount] = (Inc1) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
  590. allow1 = false;
  591. }
  592. if (allow2 && typeIdx == EcsComponentType<Inc2>.TypeIndex) {
  593. Get2[EntitiesCount] = (Inc2) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
  594. allow2 = false;
  595. }
  596. if (allow3 && typeIdx == EcsComponentType<Inc3>.TypeIndex) {
  597. Get3[EntitiesCount] = (Inc3) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
  598. allow3 = false;
  599. }
  600. if (allow4 && typeIdx == EcsComponentType<Inc4>.TypeIndex) {
  601. Get4[EntitiesCount] = (Inc4) entity.Owner.ComponentPools[typeIdx].Items[itemIdx];
  602. allow4 = false;
  603. }
  604. }
  605. Entities[EntitiesCount++] = entity;
  606. ProcessListeners (true, entity);
  607. }
  608. /// <summary>
  609. /// Event for removing non-compatible entity to filter.
  610. /// Warning: Don't call manually!
  611. /// </summary>
  612. /// <param name="entity">Entity.</param>
  613. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  614. public override void OnRemoveEntity (in EcsEntity entity) {
  615. if (AddDelayedOp (false, entity)) { return; }
  616. for (int i = 0, iMax = EntitiesCount; i < iMax; i++) {
  617. if (Entities[i] == entity) {
  618. EntitiesCount--;
  619. if (i < EntitiesCount) {
  620. Entities[i] = Entities[EntitiesCount];
  621. if (_allow1) { Get1[i] = Get1[EntitiesCount]; }
  622. if (_allow2) { Get2[i] = Get2[EntitiesCount]; }
  623. if (_allow3) { Get3[i] = Get3[EntitiesCount]; }
  624. if (_allow4) { Get4[i] = Get4[EntitiesCount]; }
  625. }
  626. ProcessListeners (false, entity);
  627. break;
  628. }
  629. }
  630. }
  631. public class Exclude<Exc1> : EcsFilter<Inc1, Inc2, Inc3, Inc4> where Exc1 : class {
  632. protected Exclude () {
  633. ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex };
  634. ExcludedTypes = new[] { EcsComponentType<Exc1>.Type };
  635. }
  636. }
  637. public class Exclude<Exc1, Exc2> : EcsFilter<Inc1, Inc2, Inc3, Inc4> where Exc1 : class where Exc2 : class {
  638. protected Exclude () {
  639. ExcludedTypeIndices = new[] { EcsComponentType<Exc1>.TypeIndex, EcsComponentType<Exc2>.TypeIndex };
  640. ExcludedTypes = new[] { EcsComponentType<Exc1>.Type, EcsComponentType<Exc2>.Type };
  641. }
  642. }
  643. }
  644. }