Entitas  0.35.0
Entitas is a super fast Entity Component System (ECS) Framework specifically made for C# and Unity
Pool.cs
1 using System;
2 using System.Collections.Generic;
3 
4 namespace Entitas {
5 
6  /// A pool manages the lifecycle of entities and groups.
7  /// You can create and destroy entities and get groups of entities.
8  /// The prefered way is to use the generated methods from the code generator
9  /// to create a Pool, e.g. Pools.sharedInstance.pool = Pools.CreatePool();
10  public partial class Pool {
11 
12  /// Occurs when an entity gets created.
13  public event PoolChanged OnEntityCreated;
14 
15  /// Occurs when an entity will be destroyed.
16  public event PoolChanged OnEntityWillBeDestroyed;
17 
18  /// Occurs when an entity got destroyed.
19  public event PoolChanged OnEntityDestroyed;
20 
21  /// Occurs when a group gets created for the first time.
22  public event GroupChanged OnGroupCreated;
23 
24  /// Occurs when a group gets cleared.
25  public event GroupChanged OnGroupCleared;
26 
27  public delegate void PoolChanged(Pool pool, Entity entity);
28  public delegate void GroupChanged(Pool pool, Group group);
29 
30  /// The total amount of components an entity can possibly have.
31  /// This value is generated by the code generator,
32  /// e.g ComponentIds.TotalComponents.
33  public int totalComponents { get { return _totalComponents; } }
34 
35  /// Returns all componentPools. componentPools is used to reuse
36  /// removed components.
37  /// Removed components will be pushed to the componentPool.
38  /// Use entity.CreateComponent(index, type) to get a new or reusable
39  /// component from the componentPool.
40  public Stack<IComponent>[] componentPools {
41  get { return _componentPools; }
42  }
43 
44  /// The metaData contains information about the pool.
45  /// It's used to provide better error messages.
46  public PoolMetaData metaData { get { return _metaData; } }
47 
48  /// Returns the number of entities in the pool.
49  public int count { get { return _entities.Count; } }
50 
51  /// Returns the number of entities in the internal ObjectPool
52  /// for entities which can be reused.
53  public int reusableEntitiesCount {
54  get { return _reusableEntities.Count; }
55  }
56 
57  /// Returns the number of entities that are currently retained by
58  /// other objects (e.g. Group, GroupObserver, ReactiveSystem).
59  public int retainedEntitiesCount {
60  get { return _retainedEntities.Count; }
61  }
62 
63  readonly int _totalComponents;
64  int _creationIndex;
65 
66  readonly HashSet<Entity> _entities = new HashSet<Entity>(
67  EntityEqualityComparer.comparer
68  );
69 
70  readonly Stack<Entity> _reusableEntities = new Stack<Entity>();
71  readonly HashSet<Entity> _retainedEntities = new HashSet<Entity>(
72  EntityEqualityComparer.comparer
73  );
74 
75  Entity[] _entitiesCache;
76 
77  readonly PoolMetaData _metaData;
78 
79  readonly Dictionary<IMatcher, Group> _groups =
80  new Dictionary<IMatcher, Group>();
81 
82  readonly List<Group>[] _groupsForIndex;
83 
84  readonly Stack<IComponent>[] _componentPools;
85  readonly Dictionary<string, IEntityIndex> _entityIndices;
86 
87  // Cache delegates to avoid gc allocations
88  Entity.EntityChanged _cachedEntityChanged;
89  Entity.ComponentReplaced _cachedComponentReplaced;
90  Entity.EntityReleased _cachedEntityReleased;
91 
92  /// The prefered way is to use the generated methods from the
93  /// code generator to create a Pool,
94  /// e.g. Pools.sharedInstance.pool = Pools.CreatePool();
95  public Pool(int totalComponents) : this(totalComponents, 0, null) {
96  }
97 
98  /// The prefered way is to use the generated methods from the
99  /// code generator to create a Pool,
100  /// e.g. Pools.sharedInstance.pool = Pools.CreatePool();
101  public Pool(int totalComponents,
102  int startCreationIndex,
104  _totalComponents = totalComponents;
105  _creationIndex = startCreationIndex;
106 
107  if(metaData != null) {
108  _metaData = metaData;
109 
110  if(metaData.componentNames.Length != totalComponents) {
111  throw new PoolMetaDataException(this, metaData);
112  }
113  } else {
114 
115  // If Pools.CreatePool() was used to create the pool,
116  // we will never end up here.
117  // This is a fallback when the pool is created manually.
118 
119  var componentNames = new string[totalComponents];
120  const string prefix = "Index ";
121  for (int i = 0; i < componentNames.Length; i++) {
122  componentNames[i] = prefix + i;
123  }
124  _metaData = new PoolMetaData(
125  "Unnamed Pool", componentNames, null
126  );
127  }
128 
129  _groupsForIndex = new List<Group>[totalComponents];
130  _componentPools = new Stack<IComponent>[totalComponents];
131  _entityIndices = new Dictionary<string, IEntityIndex>();
132 
133  // Cache delegates to avoid gc allocations
134  _cachedEntityChanged = updateGroupsComponentAddedOrRemoved;
135  _cachedComponentReplaced = updateGroupsComponentReplaced;
136  _cachedEntityReleased = onEntityReleased;
137  }
138 
139  /// Creates a new entity or gets a reusable entity from the
140  /// internal ObjectPool for entities.
141  public virtual Entity CreateEntity() {
142  var entity = _reusableEntities.Count > 0
143  ? _reusableEntities.Pop()
144  : new Entity( _totalComponents, _componentPools, _metaData);
145  entity._isEnabled = true;
146  entity._creationIndex = _creationIndex++;
147  entity.Retain(this);
148  _entities.Add(entity);
149  _entitiesCache = null;
150  entity.OnComponentAdded +=_cachedEntityChanged;
151  entity.OnComponentRemoved += _cachedEntityChanged;
152  entity.OnComponentReplaced += _cachedComponentReplaced;
153  entity.OnEntityReleased += _cachedEntityReleased;
154 
155  if(OnEntityCreated != null) {
156  OnEntityCreated(this, entity);
157  }
158 
159  return entity;
160  }
161 
162  /// Destroys the entity, removes all its components and pushs it back
163  /// to the internal ObjectPool for entities.
164  public virtual void DestroyEntity(Entity entity) {
165  var removed = _entities.Remove(entity);
166  if(!removed) {
168  "'" + this + "' cannot destroy " + entity + "!",
169  "Did you call pool.DestroyEntity() on a wrong pool?"
170  );
171  }
172  _entitiesCache = null;
173 
174  if(OnEntityWillBeDestroyed != null) {
175  OnEntityWillBeDestroyed(this, entity);
176  }
177 
178  entity.destroy();
179 
180  if(OnEntityDestroyed != null) {
181  OnEntityDestroyed(this, entity);
182  }
183 
184  if(entity.retainCount == 1) {
185  // Can be released immediately without
186  // adding to _retainedEntities
187  entity.OnEntityReleased -= _cachedEntityReleased;
188  _reusableEntities.Push(entity);
189  entity.Release(this);
190  entity.removeAllOnEntityReleasedHandlers();
191  } else {
192  _retainedEntities.Add(entity);
193  entity.Release(this);
194  }
195  }
196 
197  /// Destroys all entities in the pool.
198  /// Throws an exception if there are still retained entities.
199  public virtual void DestroyAllEntities() {
200  var entities = GetEntities();
201  for (int i = 0; i < entities.Length; i++) {
202  DestroyEntity(entities[i]);
203  }
204 
205  _entities.Clear();
206 
207  if(_retainedEntities.Count != 0) {
209  }
210  }
211 
212  /// Determines whether the pool has the specified entity.
213  public virtual bool HasEntity(Entity entity) {
214  return _entities.Contains(entity);
215  }
216 
217  /// Returns all entities which are currently in the pool.
218  public virtual Entity[] GetEntities() {
219  if(_entitiesCache == null) {
220  _entitiesCache = new Entity[_entities.Count];
221  _entities.CopyTo(_entitiesCache);
222  }
223 
224  return _entitiesCache;
225  }
226 
227  /// Returns a group for the specified matcher.
228  /// Calling pool.GetGroup(matcher) with the same matcher will always
229  /// return the same instance of the group.
230  public virtual Group GetGroup(IMatcher matcher) {
231  Group group;
232  if(!_groups.TryGetValue(matcher, out group)) {
233  group = new Group(matcher);
234  var entities = GetEntities();
235  for (int i = 0; i < entities.Length; i++) {
236  group.HandleEntitySilently(entities[i]);
237  }
238  _groups.Add(matcher, group);
239 
240  for (int i = 0; i < matcher.indices.Length; i++) {
241  var index = matcher.indices[i];
242  if(_groupsForIndex[index] == null) {
243  _groupsForIndex[index] = new List<Group>();
244  }
245  _groupsForIndex[index].Add(group);
246  }
247 
248  if(OnGroupCreated != null) {
249  OnGroupCreated(this, group);
250  }
251  }
252 
253  return group;
254  }
255 
256  /// Clears all groups. This is useful when you want to
257  /// soft-restart your application.
258  public void ClearGroups() {
259  foreach(var group in _groups.Values) {
260  group.RemoveAllEventHandlers();
261  var entities = group.GetEntities();
262  for (int i = 0; i < entities.Length; i++) {
263  entities[i].Release(group);
264  }
265 
266  if(OnGroupCleared != null) {
267  OnGroupCleared(this, group);
268  }
269  }
270  _groups.Clear();
271 
272  for (int i = 0; i < _groupsForIndex.Length; i++) {
273  _groupsForIndex[i] = null;
274  }
275  }
276 
277  /// Adds the IEntityIndex for the specified name.
278  /// There can only be one IEntityIndex per name.
279  public void AddEntityIndex(string name, IEntityIndex entityIndex) {
280  if(_entityIndices.ContainsKey(name)) {
281  throw new PoolEntityIndexDoesAlreadyExistException(this, name);
282  }
283 
284  _entityIndices.Add(name, entityIndex);
285  }
286 
287  /// Gets the IEntityIndex for the specified name.
288  public IEntityIndex GetEntityIndex(string name) {
289  IEntityIndex entityIndex;
290  if(!_entityIndices.TryGetValue(name, out entityIndex)) {
291  throw new PoolEntityIndexDoesNotExistException(this, name);
292  }
293 
294  return entityIndex;
295  }
296 
297  /// Deactivates and removes all entity indices.
299  foreach(var entityIndex in _entityIndices.Values) {
300  entityIndex.Deactivate();
301  }
302 
303  _entityIndices.Clear();
304  }
305 
306  /// Resets the creationIndex back to 0.
307  public void ResetCreationIndex() {
308  _creationIndex = 0;
309  }
310 
311  /// Clears the componentPool at the specified index.
312  public void ClearComponentPool(int index) {
313  var componentPool = _componentPools[index];
314  if(componentPool != null) {
315  componentPool.Clear();
316  }
317  }
318 
319  /// Clears all componentPools.
320  public void ClearComponentPools() {
321  for (int i = 0; i < _componentPools.Length; i++) {
323  }
324  }
325 
326  /// Resets the pool (clears all groups, destroys all entities and
327  /// resets creationIndex back to 0).
328  public void Reset() {
329  ClearGroups();
332 
333  OnEntityCreated = null;
334  OnEntityWillBeDestroyed = null;
335  OnEntityDestroyed = null;
336  OnGroupCreated = null;
337  OnGroupCleared = null;
338  }
339 
340  public override string ToString() {
341  return _metaData.poolName;
342  }
343 
344  void updateGroupsComponentAddedOrRemoved(
345  Entity entity, int index, IComponent component) {
346  var groups = _groupsForIndex[index];
347  if(groups != null) {
348  var events = EntitasCache.GetGroupChangedList();
349 
350  for(int i = 0; i < groups.Count; i++) {
351  events.Add(groups[i].handleEntity(entity));
352  }
353 
354  for(int i = 0; i < events.Count; i++) {
355  var groupChangedEvent = events[i];
356  if(groupChangedEvent != null) {
357  groupChangedEvent(
358  groups[i], entity, index, component
359  );
360  }
361  }
362 
363  EntitasCache.PushGroupChangedList(events);
364  }
365  }
366 
367  void updateGroupsComponentReplaced(Entity entity,
368  int index,
369  IComponent previousComponent,
370  IComponent newComponent) {
371  var groups = _groupsForIndex[index];
372  if(groups != null) {
373  for (int i = 0; i < groups.Count; i++) {
374  groups[i].UpdateEntity(
375  entity, index, previousComponent, newComponent
376  );
377  }
378  }
379  }
380 
381  void onEntityReleased(Entity entity) {
382  if(entity._isEnabled) {
384  "Cannot release " + entity + "!"
385  );
386  }
387  entity.removeAllOnEntityReleasedHandlers();
388  _retainedEntities.Remove(entity);
389  _reusableEntities.Push(entity);
390  }
391  }
392 
394  public PoolDoesNotContainEntityException(string message, string hint) :
395  base(message + "\nPool does not contain entity!", hint) {
396  }
397  }
398 
400  public EntityIsNotDestroyedException(string message) :
401  base(message + "\nEntity is not destroyed yet!",
402  "Did you manually call entity.Release(pool) yourself? " +
403  "If so, please don't :)") {
404  }
405  }
406 
408  public PoolStillHasRetainedEntitiesException(Pool pool) : base(
409  "'" + pool + "' detected retained entities " +
410  "although all entities got destroyed!",
411  "Did you release all entities? Try calling pool.ClearGroups() " +
412  "and systems.ClearReactiveSystems() before calling " +
413  "pool.DestroyAllEntities() to avoid memory leaks.") {
414  }
415  }
416 
418  public PoolMetaDataException(Pool pool, PoolMetaData poolMetaData) :
419  base("Invalid PoolMetaData for '" + pool + "'!\nExpected " +
420  pool.totalComponents + " componentName(s) but got " +
421  poolMetaData.componentNames.Length + ":",
422  string.Join("\n", poolMetaData.componentNames)) {
423  }
424  }
425 
427  public PoolEntityIndexDoesNotExistException(Pool pool, string name) :
428  base("Cannot get EntityIndex '" + name + "' from pool '" +
429  pool + "'!", "No EntityIndex with this name has been added.") {
430  }
431  }
432 
434  public PoolEntityIndexDoesAlreadyExistException(Pool pool, string name) :
435  base("Cannot add EntityIndex '" + name + "' to pool '" + pool + "'!",
436  "An EntityIndex with this name has already been added.") {
437  }
438  }
439 
440  /// The metaData contains information about the pool.
441  /// It's used to provide better error messages.
442  public class PoolMetaData {
443 
444  public readonly string poolName;
445  public readonly string[] componentNames;
446  public readonly Type[] componentTypes;
447 
448  public PoolMetaData(string poolName,
449  string[] componentNames,
450  Type[] componentTypes) {
451  this.poolName = poolName;
452  this.componentNames = componentNames;
453  this.componentTypes = componentTypes;
454  }
455  }
456 }
virtual bool HasEntity(Entity entity)
Determines whether the pool has the specified entity.
Definition: Pool.cs:213
int retainCount
Returns the number of objects that retain this entity.
Definition: Entity.cs:365
int count
Returns the number of entities in the pool.
Definition: Pool.cs:49
int retainedEntitiesCount
Definition: Pool.cs:59
ComponentReplaced OnComponentReplaced
Definition: Entity.cs:25
Pool(int totalComponents, int startCreationIndex, PoolMetaData metaData)
Definition: Pool.cs:101
void ClearGroups()
Definition: Pool.cs:258
int reusableEntitiesCount
Definition: Pool.cs:53
virtual Entity [] GetEntities()
Returns all entities which are currently in the pool.
Definition: Pool.cs:218
virtual void DestroyEntity(Entity entity)
Definition: Pool.cs:164
void AddEntityIndex(string name, IEntityIndex entityIndex)
Definition: Pool.cs:279
PoolChanged OnEntityCreated
Occurs when an entity gets created.
Definition: Pool.cs:13
EntityChanged OnComponentRemoved
Definition: Entity.cs:20
PoolMetaData metaData
Definition: Pool.cs:46
int totalComponents
Definition: Pool.cs:33
void ResetCreationIndex()
Resets the creationIndex back to 0.
Definition: Pool.cs:307
IEntityIndex GetEntityIndex(string name)
Gets the IEntityIndex for the specified name.
Definition: Pool.cs:288
Pool(int totalComponents)
Definition: Pool.cs:95
EntityReleased OnEntityReleased
Definition: Entity.cs:30
void DeactivateAndRemoveEntityIndices()
Deactivates and removes all entity indices.
Definition: Pool.cs:298
void Reset()
Definition: Pool.cs:328
void ClearComponentPools()
Clears all componentPools.
Definition: Pool.cs:320
virtual Group GetGroup(IMatcher matcher)
Definition: Pool.cs:230
Stack< IComponent > [] componentPools
Definition: Pool.cs:40
void ClearComponentPool(int index)
Clears the componentPool at the specified index.
Definition: Pool.cs:312
virtual Entity CreateEntity()
Definition: Pool.cs:141
PoolChanged OnEntityWillBeDestroyed
Occurs when an entity will be destroyed.
Definition: Pool.cs:16
PoolChanged OnEntityDestroyed
Occurs when an entity got destroyed.
Definition: Pool.cs:19
void Release(object owner)
Definition: Entity.cs:402
GroupChanged OnGroupCleared
Occurs when a group gets cleared.
Definition: Pool.cs:25
GroupChanged OnGroupCreated
Occurs when a group gets created for the first time.
Definition: Pool.cs:22
Base exception used by Entitas.
virtual void DestroyAllEntities()
Definition: Pool.cs:199