Entitas  0.40.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 context.CreateEntity() to create a new entity and
8  /// context.DestroyEntity() to destroy it.
9  /// You can add, replace and remove IComponent to an entity.
10  public class Entity : IEntity {
11 
12  /// Occurs when a component gets added.
13  /// All event handlers will be removed when
14  /// the entity gets destroyed by the context.
15  public event EntityComponentChanged OnComponentAdded;
16 
17  /// Occurs when a component gets removed.
18  /// All event handlers will be removed when
19  /// the entity gets destroyed by the context.
20  public event EntityComponentChanged OnComponentRemoved;
21 
22  /// Occurs when a component gets replaced.
23  /// All event handlers will be removed when
24  /// the entity gets destroyed by the context.
25  public event EntityComponentReplaced 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 context.
30  public event EntityReleased OnEntityReleased;
31 
32  /// The total amount of components an entity can possibly have.
33  public int totalComponents { get { return _totalComponents; } }
34 
35  /// Each entity has its own unique creationIndex which will be set by
36  /// the context when you create the entity.
37  public int creationIndex { get { return _creationIndex; } }
38 
39  /// The context manages the state of an entity.
40  /// Active entities are enabled, destroyed entities are not.
41  public bool isEnabled { get { return _isEnabled; } }
42 
43  /// componentPools is set by the context which created the entity and
44  /// is used to reuse removed components.
45  /// Removed components will be pushed to the componentPool.
46  /// Use entity.CreateComponent(index, type) to get a new or
47  /// reusable component from the componentPool.
48  /// Use entity.GetComponentPool(index) to get a componentPool for
49  /// a specific component index.
50  public Stack<IComponent>[] componentPools { get { return _componentPools; } }
51 
52  /// The contextInfo is set by the context which created the entity and
53  /// contains information about the context.
54  /// It's used to provide better error messages.
55  public ContextInfo contextInfo { get { return _contextInfo; } }
56 
57  int _creationIndex;
58  bool _isEnabled;
59 
60  int _totalComponents;
61  IComponent[] _components;
62  Stack<IComponent>[] _componentPools;
63  ContextInfo _contextInfo;
64 
65  IComponent[] _componentsCache;
66  int[] _componentIndicesCache;
67  string _toStringCache;
68  StringBuilder _toStringBuilder;
69 
70  public void Initialize(int creationIndex, int totalComponents, Stack<IComponent>[] componentPools, ContextInfo contextInfo = null) {
71  Reactivate(creationIndex);
72 
73  _totalComponents = totalComponents;
74  _components = new IComponent[totalComponents];
75  _componentPools = componentPools;
76 
77  _contextInfo = contextInfo ?? createDefaultContextInfo();
78  }
79 
80  ContextInfo createDefaultContextInfo() {
81  var componentNames = new string[totalComponents];
82  for(int i = 0; i < componentNames.Length; i++) {
83  componentNames[i] = i.ToString();
84  }
85 
86  return new ContextInfo("No Context", componentNames, null);
87  }
88 
89  public void Reactivate(int creationIndex) {
90  _creationIndex = creationIndex;
91  _isEnabled = true;
92  }
93 
94  /// Adds a component at the specified index.
95  /// You can only have one component at an index.
96  /// Each component type must have its own constant index.
97  /// The prefered way is to use the
98  /// generated methods from the code generator.
99  public void AddComponent(int index, IComponent component) {
100  if(!_isEnabled) {
101  throw new EntityIsNotEnabledException(
102  "Cannot add component '" +
103  _contextInfo.componentNames[index] + "' to " + this + "!"
104  );
105  }
106 
107  if(HasComponent(index)) {
109  index, "Cannot add component '" +
110  _contextInfo.componentNames[index] + "' to " + this + "!",
111  "You should check if an entity already has the component " +
112  "before adding it or use entity.ReplaceComponent()."
113  );
114  }
115 
116  _components[index] = component;
117  _componentsCache = null;
118  _componentIndicesCache = null;
119  _toStringCache = null;
120  if(OnComponentAdded != null) {
121  OnComponentAdded(this, index, component);
122  }
123  }
124 
125  /// Removes a component at the specified index.
126  /// You can only remove a component at an index if it exists.
127  /// The prefered way is to use the
128  /// generated methods from the code generator.
129  public void RemoveComponent(int index) {
130  if(!_isEnabled) {
131  throw new EntityIsNotEnabledException(
132  "Cannot remove component '" +
133  _contextInfo.componentNames[index] + "' from " + this + "!"
134  );
135  }
136 
137  if(!HasComponent(index)) {
139  index, "Cannot remove component '" +
140  _contextInfo.componentNames[index] + "' from " + this + "!",
141  "You should check if an entity has the component " +
142  "before removing it."
143  );
144  }
145 
146  replaceComponent(index, null);
147  }
148 
149  /// Replaces an existing component at the specified index
150  /// or adds it if it doesn't exist yet.
151  /// The prefered way is to use the
152  /// generated methods from the code generator.
153  public void ReplaceComponent(int index, IComponent component) {
154  if(!_isEnabled) {
155  throw new EntityIsNotEnabledException(
156  "Cannot replace component '" +
157  _contextInfo.componentNames[index] + "' on " + this + "!"
158  );
159  }
160 
161  if(HasComponent(index)) {
162  replaceComponent(index, component);
163  } else if(component != null) {
164  AddComponent(index, component);
165  }
166  }
167 
168  void replaceComponent(int index, IComponent replacement) {
169  _toStringCache = null;
170  var previousComponent = _components[index];
171  if(replacement != previousComponent) {
172  _components[index] = replacement;
173  _componentsCache = null;
174  if(replacement != null) {
175  if(OnComponentReplaced != null) {
177  this, index, previousComponent, replacement
178  );
179  }
180  } else {
181  _componentIndicesCache = null;
182  if(OnComponentRemoved != null) {
183  OnComponentRemoved(this, index, previousComponent);
184  }
185  }
186 
187  GetComponentPool(index).Push(previousComponent);
188 
189  } else {
190  if(OnComponentReplaced != null) {
192  this, index, previousComponent, replacement
193  );
194  }
195  }
196  }
197 
198  /// Returns a component at the specified index.
199  /// You can only get a component at an index if it exists.
200  /// The prefered way is to use the
201  /// generated methods from the code generator.
202  public IComponent GetComponent(int index) {
203  if(!HasComponent(index)) {
205  index, "Cannot get component '" +
206  _contextInfo.componentNames[index] + "' from " + this + "!",
207  "You should check if an entity has the component " +
208  "before getting it."
209  );
210  }
211 
212  return _components[index];
213  }
214 
215  /// Returns all added components.
217  if(_componentsCache == null) {
218  var components = EntitasCache.GetIComponentList();
219 
220  for(int i = 0; i < _components.Length; i++) {
221  var component = _components[i];
222  if(component != null) {
223  components.Add(component);
224  }
225  }
226 
227  _componentsCache = components.ToArray();
228 
229  EntitasCache.PushIComponentList(components);
230  }
231 
232  return _componentsCache;
233  }
234 
235  /// Returns all indices of added components.
236  public int[] GetComponentIndices() {
237  if(_componentIndicesCache == null) {
238  var indices = EntitasCache.GetIntList();
239 
240  for(int i = 0; i < _components.Length; i++) {
241  if(_components[i] != null) {
242  indices.Add(i);
243  }
244  }
245 
246  _componentIndicesCache = indices.ToArray();
247 
248  EntitasCache.PushIntList(indices);
249  }
250 
251  return _componentIndicesCache;
252  }
253 
254  /// Determines whether this entity has a component
255  /// at the specified index.
256  public bool HasComponent(int index) {
257  return _components[index] != null;
258  }
259 
260  /// Determines whether this entity has components
261  /// at all the specified indices.
262  public bool HasComponents(int[] indices) {
263  for(int i = 0; i < indices.Length; i++) {
264  if(_components[indices[i]] == null) {
265  return false;
266  }
267  }
268 
269  return true;
270  }
271 
272  /// Determines whether this entity has a component
273  /// at any of the specified indices.
274  public bool HasAnyComponent(int[] indices) {
275  for(int i = 0; i < indices.Length; i++) {
276  if(_components[indices[i]] != null) {
277  return true;
278  }
279  }
280 
281  return false;
282  }
283 
284  /// Removes all components.
285  public void RemoveAllComponents() {
286  _toStringCache = null;
287  for(int i = 0; i < _components.Length; i++) {
288  if(_components[i] != null) {
289  replaceComponent(i, null);
290  }
291  }
292  }
293 
294  /// Returns the componentPool for the specified component index.
295  /// componentPools is set by the context which created the entity and
296  /// is used to reuse removed components.
297  /// Removed components will be pushed to the componentPool.
298  /// Use entity.CreateComponent(index, type) to get a new or
299  /// reusable component from the componentPool.
300  public Stack<IComponent> GetComponentPool(int index) {
301  var componentPool = _componentPools[index];
302  if(componentPool == null) {
303  componentPool = new Stack<IComponent>();
304  _componentPools[index] = componentPool;
305  }
306 
307  return componentPool;
308  }
309 
310  /// Returns a new or reusable component from the componentPool
311  /// for the specified component index.
312  public IComponent CreateComponent(int index, Type type) {
313  var componentPool = GetComponentPool(index);
314  return componentPool.Count > 0
315  ? componentPool.Pop()
316  : (IComponent)Activator.CreateInstance(type);
317  }
318 
319  /// Returns a new or reusable component from the componentPool
320  /// for the specified component index.
321  public T CreateComponent<T>(int index) where T : new() {
322  var componentPool = GetComponentPool(index);
323  return componentPool.Count > 0 ? (T)componentPool.Pop() : new T();
324  }
325 
326  /// Returns the number of objects that retain this entity.
327  public int retainCount { get { return _retainCount; } }
328  int _retainCount;
329 
330 #if !ENTITAS_FAST_AND_UNSAFE
331  /// Returns all the objects that retain this entity.
332  public HashSet<object> owners { get { return _owners; } }
333  readonly HashSet<object> _owners = new HashSet<object>();
334 #endif
335 
336  /// Retains the entity. An owner can only retain the same entity once.
337  /// Retain/Release is part of AERC (Automatic Entity Reference Counting)
338  /// and is used internally to prevent pooling retained entities.
339  /// If you use retain manually you also have to
340  /// release it manually at some point.
341  public void Retain(object owner) {
342  _retainCount += 1;
343 
344 #if !ENTITAS_FAST_AND_UNSAFE
345  if(!owners.Add(owner)) {
346  throw new EntityIsAlreadyRetainedByOwnerException(this, owner);
347  }
348 #endif
349 
350  _toStringCache = null;
351  }
352 
353  /// Releases the entity. An owner can only release an entity
354  /// if it retains it.
355  /// Retain/Release is part of AERC (Automatic Entity Reference Counting)
356  /// and is used internally to prevent pooling retained entities.
357  /// If you use retain manually you also have to
358  /// release it manually at some point.
359  public void Release(object owner) {
360  _retainCount -= 1;
361  _toStringCache = null;
362 
363 #if !ENTITAS_FAST_AND_UNSAFE
364  if(!owners.Remove(owner)) {
365  throw new EntityIsNotRetainedByOwnerException(this, owner);
366  }
367 #endif
368 
369  if(_retainCount == 0) {
370  if(OnEntityReleased != null) {
371  OnEntityReleased(this);
372  }
373  }
374  }
375 
376  // This method is used internally. Don't call it yourself.
377  // Use context.DestroyEntity(entity);
378  public void Destroy() {
379  _isEnabled = false;
381  OnComponentAdded = null;
382  OnComponentReplaced = null;
383  OnComponentRemoved = null;
384  }
385 
386  // Do not call this method manually. This method is called by the context.
387  public void RemoveAllOnEntityReleasedHandlers() {
388  OnEntityReleased = null;
389  }
390 
391  /// Returns a cached string to describe the entity
392  /// with the following format:
393  /// Entity_{creationIndex}(*{retainCount})({list of components})
394  public override string ToString() {
395  if(_toStringCache == null) {
396  if(_toStringBuilder == null) {
397  _toStringBuilder = new StringBuilder();
398  }
399  _toStringBuilder.Length = 0;
400  _toStringBuilder
401  .Append("Entity_")
402  .Append(_creationIndex)
403  .Append("(*")
404  .Append(retainCount)
405  .Append(")")
406  .Append("(");
407 
408  const string separator = ", ";
409  var components = GetComponents();
410  var lastSeparator = components.Length - 1;
411  for(int i = 0; i < components.Length; i++) {
412  var component = components[i];
413  var type = component.GetType();
414  var implementsToString = type.GetMethod("ToString")
415  .DeclaringType.ImplementsInterface<IComponent>();
416  _toStringBuilder.Append(
417  implementsToString
418  ? component.ToString()
419  : type.ToCompilableString().RemoveComponentSuffix()
420  );
421 
422  if(i < lastSeparator) {
423  _toStringBuilder.Append(separator);
424  }
425  }
426 
427  _toStringBuilder.Append(")");
428  _toStringCache = _toStringBuilder.ToString();
429  }
430 
431  return _toStringCache;
432  }
433  }
434 }
ContextInfo contextInfo
Definition: Entity.cs:55
int retainCount
Returns the number of objects that retain this entity.
Definition: Entity.cs:327
EntityComponentReplaced OnComponentReplaced
Definition: Entity.cs:25
void AddComponent(int index, IComponent component)
Definition: Entity.cs:99
bool HasComponents(int[] indices)
Definition: Entity.cs:262
EntityComponentChanged OnComponentAdded
Definition: Entity.cs:15
EntityComponentChanged OnComponentRemoved
Definition: Entity.cs:20
int totalComponents
The total amount of components an entity can possibly have.
Definition: Entity.cs:33
EntityReleased OnEntityReleased
Definition: Entity.cs:30
bool HasAnyComponent(int[] indices)
Definition: Entity.cs:274
IComponent CreateComponent(int index, Type type)
Definition: Entity.cs:312
override string ToString()
Definition: Entity.cs:394
IComponent GetComponent(int index)
Definition: Entity.cs:202
IComponent [] GetComponents()
Returns all added components.
Definition: Entity.cs:216
void Retain(object owner)
Definition: Entity.cs:341
int [] GetComponentIndices()
Returns all indices of added components.
Definition: Entity.cs:236
void ReplaceComponent(int index, IComponent component)
Definition: Entity.cs:153
bool HasComponent(int index)
Definition: Entity.cs:256
HashSet< object > owners
Returns all the objects that retain this entity.
Definition: Entity.cs:332
Stack< IComponent > GetComponentPool(int index)
Definition: Entity.cs:300
bool isEnabled
Definition: Entity.cs:41
void Release(object owner)
Definition: Entity.cs:359
void RemoveAllComponents()
Removes all components.
Definition: Entity.cs:285
int creationIndex
Definition: Entity.cs:37
void RemoveComponent(int index)
Definition: Entity.cs:129
Stack< IComponent > [] componentPools
Definition: Entity.cs:50
T CreateComponent< T >(int index)
Definition: Entity.cs:321