Entitas  0.35.0
Entitas is a super fast Entity Component System (ECS) Framework specifically made for C# and Unity
Entity.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 
5 namespace Entitas {
6 
7  /// Use pool.CreateEntity() to create a new entity and
8  /// pool.DestroyEntity() to destroy it.
9  /// You can add, replace and remove IComponent to an entity.
10  public partial class Entity {
11 
12  /// Occurs when a component gets added.
13  /// All event handlers will be removed when
14  /// the entity gets destroyed by the pool.
15  public event EntityChanged OnComponentAdded;
16 
17  /// Occurs when a component gets removed.
18  /// All event handlers will be removed when
19  /// the entity gets destroyed by the pool.
20  public event EntityChanged OnComponentRemoved;
21 
22  /// Occurs when a component gets replaced.
23  /// All event handlers will be removed when
24  /// the entity gets destroyed by the pool.
25  public event ComponentReplaced OnComponentReplaced;
26 
27  /// Occurs when an entity gets released and is not retained anymore.
28  /// All event handlers will be removed when
29  /// the entity gets destroyed by the pool.
30  public event EntityReleased OnEntityReleased;
31 
32  public delegate void EntityChanged(
33  Entity entity, int index, IComponent component
34  );
35 
36  public delegate void ComponentReplaced(
37  Entity entity, int index,
38  IComponent previousComponent, IComponent newComponent
39  );
40 
41  public delegate void EntityReleased(Entity entity);
42 
43  /// The total amount of components an entity can possibly have.
44  public int totalComponents { get { return _totalComponents; } }
45 
46  /// Each entity has its own unique creationIndex which will be set by
47  /// the pool when you create the entity.
48  public int creationIndex { get { return _creationIndex; } }
49 
50  /// The pool manages the state of an entity.
51  /// Active entities are enabled, destroyed entities are not.
52  public bool isEnabled { get { return _isEnabled; } }
53 
54  /// componentPools is set by the pool which created the entity and
55  /// is used to reuse removed components.
56  /// Removed components will be pushed to the componentPool.
57  /// Use entity.CreateComponent(index, type) to get a new or
58  /// reusable component from the componentPool.
59  /// Use entity.GetComponentPool(index) to get a componentPool for
60  /// a specific component index.
61  public Stack<IComponent>[] componentPools {
62  get { return _componentPools; }
63  }
64 
65  /// The poolMetaData is set by the pool which created the entity and
66  /// contains information about the pool.
67  /// It's used to provide better error messages.
68  public PoolMetaData poolMetaData { get { return _poolMetaData; } }
69 
70  internal int _creationIndex;
71  internal bool _isEnabled = true;
72 
73  readonly int _totalComponents;
74  readonly IComponent[] _components;
75  readonly Stack<IComponent>[] _componentPools;
76  readonly PoolMetaData _poolMetaData;
77 
78  IComponent[] _componentsCache;
79  int[] _componentIndicesCache;
80  string _toStringCache;
81 
82  /// Use pool.CreateEntity() to create a new entity and
83  /// pool.DestroyEntity() to destroy it.
84  public Entity(int totalComponents,
85  Stack<IComponent>[] componentPools,
86  PoolMetaData poolMetaData = null) {
87  _totalComponents = totalComponents;
88  _components = new IComponent[totalComponents];
89  _componentPools = componentPools;
90 
91  if(poolMetaData != null) {
92  _poolMetaData = poolMetaData;
93  } else {
94 
95  // If pool.CreateEntity() was used to create the entity,
96  // we will never end up here.
97  // This is a fallback when entities are created manually.
98 
99  var componentNames = new string[totalComponents];
100  for(int i = 0; i < componentNames.Length; i++) {
101  componentNames[i] = i.ToString();
102  }
103  _poolMetaData = new PoolMetaData(
104  "No Pool", componentNames, null
105  );
106  }
107  }
108 
109  /// Adds a component at the specified index.
110  /// You can only have one component at an index.
111  /// Each component type must have its own constant index.
112  /// The prefered way is to use the
113  /// generated methods from the code generator.
114  public Entity AddComponent(int index, IComponent component) {
115  if(!_isEnabled) {
116  throw new EntityIsNotEnabledException(
117  "Cannot add component '" +
118  _poolMetaData.componentNames[index] +
119  "' to " + this + "!"
120  );
121  }
122 
123  if(HasComponent(index)) {
125  index,
126  "Cannot add component '" +
127  _poolMetaData.componentNames[index] +
128  "' to " + this + "!",
129  "You should check if an entity already has the component " +
130  "before adding it or use entity.ReplaceComponent()."
131  );
132  }
133 
134  _components[index] = component;
135  _componentsCache = null;
136  _componentIndicesCache = null;
137  _toStringCache = null;
138  if(OnComponentAdded != null) {
139  OnComponentAdded(this, index, component);
140  }
141 
142  return this;
143  }
144 
145  /// Removes a component at the specified index.
146  /// You can only remove a component at an index if it exists.
147  /// The prefered way is to use the
148  /// generated methods from the code generator.
149  public Entity RemoveComponent(int index) {
150  if(!_isEnabled) {
151  throw new EntityIsNotEnabledException(
152  "Cannot remove component '" +
153  _poolMetaData.componentNames[index] +
154  "' from " + this + "!"
155  );
156  }
157 
158  if(!HasComponent(index)) {
160  index,
161  "Cannot remove component '" +
162  _poolMetaData.componentNames[index] +
163  "' from " + this + "!",
164  "You should check if an entity has the component" +
165  "before removing it."
166  );
167  }
168 
169  replaceComponent(index, null);
170 
171  return this;
172  }
173 
174  /// Replaces an existing component at the specified index
175  /// or adds it if it doesn't exist yet.
176  /// The prefered way is to use the
177  /// generated methods from the code generator.
178  public Entity ReplaceComponent(int index, IComponent component) {
179  if(!_isEnabled) {
180  throw new EntityIsNotEnabledException(
181  "Cannot replace component '" +
182  _poolMetaData.componentNames[index] +
183  "' on " + this + "!"
184  );
185  }
186 
187  if(HasComponent(index)) {
188  replaceComponent(index, component);
189  } else if(component != null) {
190  AddComponent(index, component);
191  }
192 
193  return this;
194  }
195 
196  void replaceComponent(int index, IComponent replacement) {
197  var previousComponent = _components[index];
198  if(replacement != previousComponent) {
199  _components[index] = replacement;
200  _componentsCache = null;
201  if(replacement != null) {
202  if(OnComponentReplaced != null) {
204  this, index, previousComponent, replacement
205  );
206  }
207  } else {
208  _componentIndicesCache = null;
209  _toStringCache = null;
210  if(OnComponentRemoved != null) {
211  OnComponentRemoved(this, index, previousComponent);
212  }
213  }
214 
215  GetComponentPool(index).Push(previousComponent);
216 
217  } else {
218  if(OnComponentReplaced != null) {
220  this, index, previousComponent, replacement
221  );
222  }
223  }
224  }
225 
226  /// Returns a component at the specified index.
227  /// You can only get a component at an index if it exists.
228  /// The prefered way is to use the
229  /// generated methods from the code generator.
230  public IComponent GetComponent(int index) {
231  if(!HasComponent(index)) {
233  index,
234  "Cannot get component '" +
235  _poolMetaData.componentNames[index] + "' from " +
236  this + "!",
237  "You should check if an entity has the component" +
238  "before getting it."
239  );
240  }
241 
242  return _components[index];
243  }
244 
245  /// Returns all added components.
247  if(_componentsCache == null) {
248  var components = EntitasCache.GetIComponentList();
249 
250  for(int i = 0; i < _components.Length; i++) {
251  var component = _components[i];
252  if(component != null) {
253  components.Add(component);
254  }
255  }
256 
257  _componentsCache = components.ToArray();
258 
259  EntitasCache.PushIComponentList(components);
260  }
261 
262  return _componentsCache;
263  }
264 
265  /// Returns all indices of added components.
266  public int[] GetComponentIndices() {
267  if(_componentIndicesCache == null) {
268  var indices = EntitasCache.GetIntList();
269 
270  for(int i = 0; i < _components.Length; i++) {
271  if(_components[i] != null) {
272  indices.Add(i);
273  }
274  }
275 
276  _componentIndicesCache = indices.ToArray();
277 
278  EntitasCache.PushIntList(indices);
279  }
280 
281  return _componentIndicesCache;
282  }
283 
284  /// Determines whether this entity has a component
285  /// at the specified index.
286  public bool HasComponent(int index) {
287  return _components[index] != null;
288  }
289 
290  /// Determines whether this entity has components
291  /// at all the specified indices.
292  public bool HasComponents(int[] indices) {
293  for(int i = 0; i < indices.Length; i++) {
294  if(_components[indices[i]] == null) {
295  return false;
296  }
297  }
298 
299  return true;
300  }
301 
302  /// Determines whether this entity has a component
303  /// at any of the specified indices.
304  public bool HasAnyComponent(int[] indices) {
305  for(int i = 0; i < indices.Length; i++) {
306  if(_components[indices[i]] != null) {
307  return true;
308  }
309  }
310 
311  return false;
312  }
313 
314  /// Removes all components.
315  public void RemoveAllComponents() {
316  _toStringCache = null;
317  for(int i = 0; i < _components.Length; i++) {
318  if(_components[i] != null) {
319  replaceComponent(i, null);
320  }
321  }
322  }
323 
324  /// Returns the componentPool for the specified component index.
325  /// componentPools is set by the pool which created the entity and
326  /// is used to reuse removed components.
327  /// Removed components will be pushed to the componentPool.
328  /// Use entity.CreateComponent(index, type) to get a new or
329  /// reusable component from the componentPool.
330  public Stack<IComponent> GetComponentPool(int index) {
331  var componentPool = _componentPools[index];
332  if(componentPool == null) {
333  componentPool = new Stack<IComponent>();
334  _componentPools[index] = componentPool;
335  }
336 
337  return componentPool;
338  }
339 
340  /// Returns a new or reusable component from the componentPool
341  /// for the specified component index.
342  public IComponent CreateComponent(int index, Type type) {
343  var componentPool = GetComponentPool(index);
344  return componentPool.Count > 0
345  ? componentPool.Pop()
346  : (IComponent)Activator.CreateInstance(type);
347  }
348 
349  /// Returns a new or reusable component from the componentPool
350  /// for the specified component index.
351  public T CreateComponent<T>(int index) where T : new() {
352  var componentPool = GetComponentPool(index);
353  return componentPool.Count > 0 ? (T)componentPool.Pop() : new T();
354  }
355 
356 #if ENTITAS_FAST_AND_UNSAFE
357 
358  /// Returns the number of objects that retain this entity.
359  public int retainCount { get { return _retainCount; } }
360  int _retainCount;
361 
362 #else
363 
364  /// Returns the number of objects that retain this entity.
365  public int retainCount { get { return owners.Count; } }
366 
367  /// Returns all the objects that retain this entity.
368  public readonly HashSet<object> owners = new HashSet<object>();
369 
370 #endif
371 
372  /// Retains the entity. An owner can only retain the same entity once.
373  /// Retain/Release is part of AERC (Automatic Entity Reference Counting)
374  /// and is used internally to prevent pooling retained entities.
375  /// If you use retain manually you also have to
376  /// release it manually at some point.
377  public Entity Retain(object owner) {
378 
379 #if ENTITAS_FAST_AND_UNSAFE
380 
381  _retainCount += 1;
382 
383 #else
384 
385  if(!owners.Add(owner)) {
386  throw new EntityIsAlreadyRetainedByOwnerException(this, owner);
387  }
388 
389 #endif
390 
391  _toStringCache = null;
392 
393  return this;
394  }
395 
396  /// Releases the entity. An owner can only release an entity
397  /// if it retains it.
398  /// Retain/Release is part of AERC (Automatic Entity Reference Counting)
399  /// and is used internally to prevent pooling retained entities.
400  /// If you use retain manually you also have to
401  /// release it manually at some point.
402  public void Release(object owner) {
403 
404 #if ENTITAS_FAST_AND_UNSAFE
405 
406  _retainCount -= 1;
407  if(_retainCount == 0) {
408 
409 #else
410 
411  if(!owners.Remove(owner)) {
412  throw new EntityIsNotRetainedByOwnerException(this, owner);
413  }
414 
415  if(owners.Count == 0) {
416 
417 #endif
418 
419  _toStringCache = null;
420 
421  if(OnEntityReleased != null) {
422  OnEntityReleased(this);
423  }
424  }
425  }
426 
427  // This method is used internally. Don't call it yourself.
428  // Use pool.DestroyEntity(entity);
429  internal void destroy() {
430  _isEnabled = false;
432  OnComponentAdded = null;
433  OnComponentReplaced = null;
434  OnComponentRemoved = null;
435  }
436 
437  // Do not call this method manually. This method is called by the pool.
438  internal void removeAllOnEntityReleasedHandlers() {
439  OnEntityReleased = null;
440  }
441 
442  /// Returns a cached string to describe the entity
443  /// with the following format:
444  /// Entity_{creationIndex}(*{retainCount})({list of components})
445  public override string ToString() {
446  if(_toStringCache == null) {
447  var sb = new StringBuilder()
448  .Append("Entity_")
449  .Append(_creationIndex)
450  .Append("(*")
451  .Append(retainCount)
452  .Append(")")
453  .Append("(");
454 
455  const string separator = ", ";
456  var components = GetComponents();
457  var lastSeparator = components.Length - 1;
458  for(int i = 0; i < components.Length; i++) {
459  sb.Append(
460  components[i].GetType().Name.RemoveComponentSuffix()
461  );
462  if(i < lastSeparator) {
463  sb.Append(separator);
464  }
465  }
466 
467  sb.Append(")");
468  _toStringCache = sb.ToString();
469  }
470 
471  return _toStringCache;
472  }
473  }
474 
476 
478  int index, string message, string hint
479  ) : base(
480  message +
481  "\nEntity already has a component at index "
482  + index + "!",
483  hint
484  ) {
485  }
486  }
487 
489 
491  int index, string message, string hint
492  ) : base(
493  message +
494  "\nEntity does not have a component at index "
495  + index + "!",
496  hint
497  ) {
498  }
499  }
500 
502 
503  public EntityIsNotEnabledException(string message) :
504  base(
505  message + "\nEntity is not enabled!",
506  "The entity has already been destroyed. " +
507  "You cannot modify destroyed entities."
508  ) {
509  }
510  }
511 
512  public class EntityEqualityComparer : IEqualityComparer<Entity> {
513 
514  public static readonly EntityEqualityComparer comparer =
516 
517  public bool Equals(Entity x, Entity y) {
518  return x == y;
519  }
520 
521  public int GetHashCode(Entity obj) {
522  return obj._creationIndex;
523  }
524  }
525 
527 
529  Entity entity, object owner
530  ) : base(
531  "'" + owner + "' cannot retain " + entity + "!\n" +
532  "Entity is already retained by this object!",
533  "The entity must be released by this object first."
534  ) {
535  }
536  }
537 
539 
540  public EntityIsNotRetainedByOwnerException(Entity entity, object owner) :
541  base(
542  "'" + owner + "' cannot release " + entity + "!\n" +
543  "Entity is not retained by this object!",
544  "An entity can only be released from objects that retain it."
545  ) {
546  }
547  }
548 }
int retainCount
Returns the number of objects that retain this entity.
Definition: Entity.cs:365
ComponentReplaced OnComponentReplaced
Definition: Entity.cs:25
bool HasComponents(int[] indices)
Definition: Entity.cs:292
Entity ReplaceComponent(int index, IComponent component)
Definition: Entity.cs:178
EntityChanged OnComponentRemoved
Definition: Entity.cs:20
readonly HashSet< object > owners
Returns all the objects that retain this entity.
Definition: Entity.cs:368
int totalComponents
The total amount of components an entity can possibly have.
Definition: Entity.cs:44
EntityReleased OnEntityReleased
Definition: Entity.cs:30
bool HasAnyComponent(int[] indices)
Definition: Entity.cs:304
Entity(int totalComponents, Stack< IComponent >[] componentPools, PoolMetaData poolMetaData=null)
Definition: Entity.cs:84
EntityChanged OnComponentAdded
Definition: Entity.cs:15
IComponent CreateComponent(int index, Type type)
Definition: Entity.cs:342
override string ToString()
Definition: Entity.cs:445
IComponent GetComponent(int index)
Definition: Entity.cs:230
IComponent [] GetComponents()
Returns all added components.
Definition: Entity.cs:246
int [] GetComponentIndices()
Returns all indices of added components.
Definition: Entity.cs:266
bool HasComponent(int index)
Definition: Entity.cs:286
PoolMetaData poolMetaData
Definition: Entity.cs:68
Stack< IComponent > GetComponentPool(int index)
Definition: Entity.cs:330
bool isEnabled
Definition: Entity.cs:52
void Release(object owner)
Definition: Entity.cs:402
void RemoveAllComponents()
Removes all components.
Definition: Entity.cs:315
int creationIndex
Definition: Entity.cs:48
Entity RemoveComponent(int index)
Definition: Entity.cs:149
Entity AddComponent(int index, IComponent component)
Definition: Entity.cs:114
Stack< IComponent > [] componentPools
Definition: Entity.cs:61
Base exception used by Entitas.
T CreateComponent< T >(int index)
Definition: Entity.cs:351
Entity Retain(object owner)
Definition: Entity.cs:377