EcsEntity.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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. namespace Leopotam.Ecs {
  9. /// <summary>
  10. /// Entity descriptor.
  11. /// </summary>
  12. public struct EcsEntity {
  13. internal int Id;
  14. internal ushort Gen;
  15. internal EcsWorld Owner;
  16. public static readonly EcsEntity Null = new EcsEntity ();
  17. /// <summary>
  18. /// Attaches or finds already attached component to entity.
  19. /// </summary>
  20. /// <typeparam name="T">Type of component.</typeparam>
  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. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  26. public T Set<T> () where T : class {
  27. ref var entityData = ref Owner.GetEntityData (this);
  28. #if DEBUG
  29. if (entityData.Gen != Gen) { throw new Exception ("Cant add component to destroyed entity."); }
  30. #endif
  31. var typeIdx = EcsComponentType<T>.TypeIndex;
  32. // check already attached components.
  33. for (int i = 0, iiMax = entityData.ComponentsCountX2; i < iiMax; i += 2) {
  34. if (entityData.Components[i] == typeIdx) {
  35. return (T) Owner.ComponentPools[typeIdx].Items[entityData.Components[i + 1]];
  36. }
  37. }
  38. // attach new component.
  39. if (entityData.Components.Length == entityData.ComponentsCountX2) {
  40. Array.Resize (ref entityData.Components, entityData.ComponentsCountX2 << 1);
  41. }
  42. entityData.Components[entityData.ComponentsCountX2++] = typeIdx;
  43. var pool = Owner.GetPool<T> ();
  44. var idx = pool.New ();
  45. entityData.Components[entityData.ComponentsCountX2++] = idx;
  46. #if DEBUG
  47. for (var ii = 0; ii < Owner.DebugListeners.Count; ii++) {
  48. Owner.DebugListeners[ii].OnComponentListChanged (this);
  49. }
  50. #endif
  51. Owner.UpdateFilters (typeIdx, this, entityData);
  52. return (T) pool.Items[idx];
  53. }
  54. /// <summary>
  55. /// Gets component attached to entity or null.
  56. /// </summary>
  57. /// <typeparam name="T">Type of component.</typeparam>
  58. #if ENABLE_IL2CPP
  59. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
  60. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
  61. #endif
  62. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  63. public T Get<T> () where T : class {
  64. ref var entityData = ref Owner.GetEntityData (this);
  65. #if DEBUG
  66. if (entityData.Gen != Gen) { throw new Exception ("Cant check component on destroyed entity."); }
  67. #endif
  68. var typeIdx = EcsComponentType<T>.TypeIndex;
  69. for (int i = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2) {
  70. if (entityData.Components[i] == typeIdx) {
  71. return (T) Owner.ComponentPools[typeIdx].Items[entityData.Components[i + 1]];
  72. }
  73. }
  74. return null;
  75. }
  76. /// <summary>
  77. /// Removes component from entity.
  78. /// </summary>
  79. /// <typeparam name="T">Type of component.</typeparam>
  80. #if ENABLE_IL2CPP
  81. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
  82. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
  83. #endif
  84. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  85. public void Unset<T> () where T : class {
  86. Unset (EcsComponentType<T>.TypeIndex);
  87. }
  88. /// <summary>
  89. /// Removes component from entity.
  90. /// </summary>
  91. #if ENABLE_IL2CPP
  92. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
  93. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
  94. #endif
  95. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  96. internal void Unset (int typeIndex) {
  97. ref var entityData = ref Owner.GetEntityData (this);
  98. // save copy to local var for protect from cleanup fields outside.
  99. var owner = Owner;
  100. #if DEBUG
  101. if (entityData.Gen != Gen) { throw new Exception ("Cant touch destroyed entity."); }
  102. #endif
  103. for (int i = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2) {
  104. if (entityData.Components[i] == typeIndex) {
  105. owner.UpdateFilters (-typeIndex, this, entityData);
  106. owner.ComponentPools[typeIndex].Recycle (entityData.Components[i + 1]);
  107. // remove current item and move last component to this gap.
  108. entityData.ComponentsCountX2 -= 2;
  109. if (i < entityData.ComponentsCountX2) {
  110. entityData.Components[i] = entityData.Components[entityData.ComponentsCountX2];
  111. entityData.Components[i + 1] = entityData.Components[entityData.ComponentsCountX2 + 1];
  112. }
  113. #if DEBUG
  114. for (var ii = 0; ii < Owner.DebugListeners.Count; ii++) {
  115. Owner.DebugListeners[ii].OnComponentListChanged (this);
  116. }
  117. #endif
  118. break;
  119. }
  120. }
  121. // unrolled and inlined Destroy() call.
  122. if (entityData.ComponentsCountX2 == 0) {
  123. owner.RecycleEntityData (Id, ref entityData);
  124. #if DEBUG
  125. for (var ii = 0; ii < Owner.DebugListeners.Count; ii++) {
  126. owner.DebugListeners[ii].OnEntityDestroyed (this);
  127. }
  128. #endif
  129. }
  130. }
  131. /// <summary>
  132. /// Gets component index at component pool.
  133. /// If component doesn't exists "-1" will be returned.
  134. /// </summary>
  135. /// <typeparam name="T">Type of component.</typeparam>
  136. #if ENABLE_IL2CPP
  137. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
  138. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
  139. #endif
  140. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  141. public int GetComponentIndexInPool<T> () where T : class {
  142. ref var entityData = ref Owner.GetEntityData (this);
  143. #if DEBUG
  144. if (entityData.Gen != Gen) { throw new Exception ("Cant check component on destroyed entity."); }
  145. #endif
  146. var typeIdx = EcsComponentType<T>.TypeIndex;
  147. for (int i = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2) {
  148. if (entityData.Components[i] == typeIdx) {
  149. return entityData.Components[i + 1];
  150. }
  151. }
  152. return -1;
  153. }
  154. /// <summary>
  155. /// Gets internal identifier.
  156. /// </summary>
  157. public int GetInternalId () {
  158. return Id;
  159. }
  160. /// <summary>
  161. /// Removes components from entity and destroys it.
  162. /// </summary>
  163. #if ENABLE_IL2CPP
  164. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
  165. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
  166. #endif
  167. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  168. public void Destroy () {
  169. ref var entityData = ref Owner.GetEntityData (this);
  170. // save copy to local var for protect from cleanup fields outside.
  171. EcsEntity savedEntity;
  172. savedEntity.Id = Id;
  173. savedEntity.Gen = Gen;
  174. savedEntity.Owner = Owner;
  175. #if DEBUG
  176. if (entityData.Gen != Gen) { throw new Exception ("Cant touch destroyed entity."); }
  177. #endif
  178. // remove components first.
  179. for (var i = entityData.ComponentsCountX2 - 2; i >= 0; i -= 2) {
  180. savedEntity.Owner.UpdateFilters (-entityData.Components[i], savedEntity, entityData);
  181. savedEntity.Owner.ComponentPools[entityData.Components[i]].Recycle (entityData.Components[i + 1]);
  182. entityData.ComponentsCountX2 -= 2;
  183. #if DEBUG
  184. for (var ii = 0; ii < savedEntity.Owner.DebugListeners.Count; ii++) {
  185. savedEntity.Owner.DebugListeners[ii].OnComponentListChanged (savedEntity);
  186. }
  187. #endif
  188. }
  189. entityData.ComponentsCountX2 = 0;
  190. savedEntity.Owner.RecycleEntityData (savedEntity.Id, ref entityData);
  191. #if DEBUG
  192. for (var ii = 0; ii < savedEntity.Owner.DebugListeners.Count; ii++) {
  193. savedEntity.Owner.DebugListeners[ii].OnEntityDestroyed (savedEntity);
  194. }
  195. #endif
  196. }
  197. /// <summary>
  198. /// Is entity null-ed.
  199. /// </summary>
  200. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  201. public bool IsNull () {
  202. return Id == 0 && Gen == 0;
  203. }
  204. /// <summary>
  205. /// Is entity alive.
  206. /// </summary>
  207. #if ENABLE_IL2CPP
  208. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
  209. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
  210. #endif
  211. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  212. public bool IsAlive () {
  213. if (Owner == null) { return false; }
  214. ref var entityData = ref Owner.GetEntityData (this);
  215. return entityData.Gen == Gen && entityData.ComponentsCountX2 >= 0;
  216. }
  217. /// <summary>
  218. /// Gets components count on entity.
  219. /// </summary>
  220. #if ENABLE_IL2CPP
  221. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.NullChecks, false)]
  222. [Unity.IL2CPP.CompilerServices.Il2CppSetOption (Unity.IL2CPP.CompilerServices.Option.ArrayBoundsChecks, false)]
  223. #endif
  224. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  225. public int GetComponentsCount () {
  226. ref var entityData = ref Owner.GetEntityData (this);
  227. #if DEBUG
  228. if (entityData.Gen != Gen) { throw new Exception ("Cant touch destroyed entity."); }
  229. #endif
  230. return entityData.ComponentsCountX2 <= 0 ? 0 : (entityData.ComponentsCountX2 >> 1);
  231. }
  232. /// <summary>
  233. /// Gets all components on entity.
  234. /// </summary>
  235. /// <param name="list">List to put results in it. if null - will be created.</param>
  236. /// <returns>Amount of components in list.</returns>
  237. public int GetComponents (ref object[] list) {
  238. ref var entityData = ref Owner.GetEntityData (this);
  239. #if DEBUG
  240. if (entityData.Gen != Gen) { throw new Exception ("Cant touch destroyed entity."); }
  241. #endif
  242. var itemsCount = entityData.ComponentsCountX2 >> 1;
  243. if (list == null || list.Length < itemsCount) {
  244. list = new object[itemsCount];
  245. }
  246. for (int i = 0, j = 0, iMax = entityData.ComponentsCountX2; i < iMax; i += 2, j++) {
  247. list[j] = Owner.ComponentPools[entityData.Components[i]].GetItem (entityData.Components[i + 1]);
  248. }
  249. return itemsCount;
  250. }
  251. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  252. public static bool operator == (in EcsEntity lhs, in EcsEntity rhs) {
  253. return lhs.Id == rhs.Id && lhs.Gen == rhs.Gen;
  254. }
  255. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  256. public static bool operator != (in EcsEntity lhs, in EcsEntity rhs) {
  257. return lhs.Id != rhs.Id || lhs.Gen != rhs.Gen;
  258. }
  259. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  260. public override int GetHashCode () {
  261. // ReSharper disable NonReadonlyMemberInGetHashCode
  262. // not readonly for performance reason - no ctor calls for EcsEntity struct.
  263. return Id.GetHashCode () ^ (Gen.GetHashCode () << 2);
  264. // ReSharper restore NonReadonlyMemberInGetHashCode
  265. }
  266. [MethodImpl (MethodImplOptions.AggressiveInlining)]
  267. public override bool Equals (object other) {
  268. if (!(other is EcsEntity)) {
  269. return false;
  270. }
  271. var rhs = (EcsEntity) other;
  272. return Id == rhs.Id && Gen == rhs.Gen;
  273. }
  274. #if DEBUG
  275. public override string ToString () {
  276. return IsNull () ? "Entity-Null" : $"Entity-{Id}:{Gen}";
  277. }
  278. #endif
  279. }
  280. }