Entitas  0.40.0
Entitas is a super fast Entity Component System (ECS) Framework specifically made for C# and Unity
Context.cs
1 using System.Collections.Generic;
2 
3 namespace Entitas {
4 
5  /// A context manages the lifecycle of entities and groups.
6  /// You can create and destroy entities and get groups of entities.
7  /// The prefered way to create a context is to use the generated methods
8  /// from the code generator, e.g. var context = new GameContext();
9  public class Context<TEntity> : IContext<TEntity> where TEntity : class, IEntity, new() {
10 
11  /// Occurs when an entity gets created.
12  public event ContextEntityChanged OnEntityCreated;
13 
14  /// Occurs when an entity will be destroyed.
15  public event ContextEntityChanged OnEntityWillBeDestroyed;
16 
17  /// Occurs when an entity got destroyed.
18  public event ContextEntityChanged OnEntityDestroyed;
19 
20  /// Occurs when a group gets created for the first time.
21  public event ContextGroupChanged OnGroupCreated;
22 
23  /// Occurs when a group gets cleared.
24  public event ContextGroupChanged OnGroupCleared;
25 
26  /// The total amount of components an entity can possibly have.
27  /// This value is generated by the code generator,
28  /// e.g ComponentLookup.TotalComponents.
29  public int totalComponents { get { return _totalComponents; } }
30 
31  /// Returns all componentPools. componentPools is used to reuse
32  /// removed components.
33  /// Removed components will be pushed to the componentPool.
34  /// Use entity.CreateComponent(index, type) to get a new or reusable
35  /// component from the componentPool.
36  public Stack<IComponent>[] componentPools { get { return _componentPools; } }
37 
38  /// The contextInfo contains information about the context.
39  /// It's used to provide better error messages.
40  public ContextInfo contextInfo { get { return _contextInfo; } }
41 
42  /// Returns the number of entities in the context.
43  public int count { get { return _entities.Count; } }
44 
45  /// Returns the number of entities in the internal ObjectPool
46  /// for entities which can be reused.
47  public int reusableEntitiesCount { get { return _reusableEntities.Count; } }
48 
49  /// Returns the number of entities that are currently retained by
50  /// other objects (e.g. Group, Collector, ReactiveSystem).
51  public int retainedEntitiesCount { get { return _retainedEntities.Count; } }
52 
53  readonly int _totalComponents;
54 
55  readonly Stack<IComponent>[] _componentPools;
56  readonly ContextInfo _contextInfo;
57 
58  readonly HashSet<TEntity> _entities = new HashSet<TEntity>(EntityEqualityComparer<TEntity>.comparer);
59  readonly Stack<TEntity> _reusableEntities = new Stack<TEntity>();
60  readonly HashSet<TEntity> _retainedEntities = new HashSet<TEntity>(EntityEqualityComparer<TEntity>.comparer);
61 
62  readonly Dictionary<IMatcher<TEntity>, IGroup<TEntity>> _groups = new Dictionary<IMatcher<TEntity>, IGroup<TEntity>>();
63  readonly List<IGroup<TEntity>>[] _groupsForIndex;
64  readonly ObjectPool<List<GroupChanged<TEntity>>> _groupChangedListPool;
65 
66  readonly Dictionary<string, IEntityIndex> _entityIndices;
67 
68  int _creationIndex;
69 
70  TEntity[] _entitiesCache;
71 
72  // Cache delegates to avoid gc allocations
73  EntityComponentChanged _cachedEntityChanged;
74  EntityComponentReplaced _cachedComponentReplaced;
75  EntityReleased _cachedEntityReleased;
76 
77  /// The prefered way to create a context is to use the generated methods
78  /// from the code generator, e.g. var context = new GameContext();
79  public Context(int totalComponents) : this(totalComponents, 0, null) {
80  }
81 
82  /// The prefered way to create a context is to use the generated methods
83  /// from the code generator, e.g. var context = new GameContext();
84  public Context(int totalComponents, int startCreationIndex, ContextInfo contextInfo) {
85  _totalComponents = totalComponents;
86  _creationIndex = startCreationIndex;
87 
88  if(contextInfo != null) {
89  _contextInfo = contextInfo;
90  if(contextInfo.componentNames.Length != totalComponents) {
91  throw new ContextInfoException(this, contextInfo);
92  }
93  } else {
94  _contextInfo = createDefaultContextInfo();
95  }
96 
97  _groupsForIndex = new List<IGroup<TEntity>>[totalComponents];
98  _componentPools = new Stack<IComponent>[totalComponents];
99  _entityIndices = new Dictionary<string, IEntityIndex>();
100  _groupChangedListPool = new ObjectPool<List<GroupChanged<TEntity>>>(
101  () => new List<GroupChanged<TEntity>>(),
102  list => list.Clear()
103  );
104 
105  // Cache delegates to avoid gc allocations
106  _cachedEntityChanged = updateGroupsComponentAddedOrRemoved;
107  _cachedComponentReplaced = updateGroupsComponentReplaced;
108  _cachedEntityReleased = onEntityReleased;
109  }
110 
111  ContextInfo createDefaultContextInfo() {
112  var componentNames = new string[_totalComponents];
113  const string prefix = "Index ";
114  for(int i = 0; i < componentNames.Length; i++) {
115  componentNames[i] = prefix + i;
116  }
117 
118  return new ContextInfo("Unnamed Context", componentNames, null);
119  }
120 
121  /// Creates a new entity or gets a reusable entity from the
122  /// internal ObjectPool for entities.
123  public TEntity CreateEntity() {
124  TEntity entity;
125 
126  if(_reusableEntities.Count > 0) {
127  entity = _reusableEntities.Pop();
128  entity.Reactivate(_creationIndex++);
129  } else {
130  entity = new TEntity();
131  entity.Initialize(_creationIndex++, _totalComponents, _componentPools, _contextInfo);
132  }
133 
134  _entities.Add(entity);
135  entity.Retain(this);
136  _entitiesCache = null;
137  entity.OnComponentAdded += _cachedEntityChanged;
138  entity.OnComponentRemoved += _cachedEntityChanged;
139  entity.OnComponentReplaced += _cachedComponentReplaced;
140  entity.OnEntityReleased += _cachedEntityReleased;
141 
142  if(OnEntityCreated != null) {
143  OnEntityCreated(this, entity);
144  }
145 
146  return entity;
147  }
148 
149  /// Destroys the entity, removes all its components and pushs it back
150  /// to the internal ObjectPool for entities.
151  public void DestroyEntity(TEntity entity) {
152  var removed = _entities.Remove(entity);
153  if(!removed) {
155  "'" + this + "' cannot destroy " + entity + "!",
156  "Did you call context.DestroyEntity() on a wrong context?"
157  );
158  }
159  _entitiesCache = null;
160 
161  if(OnEntityWillBeDestroyed != null) {
162  OnEntityWillBeDestroyed(this, entity);
163  }
164 
165  entity.Destroy();
166 
167  if(OnEntityDestroyed != null) {
168  OnEntityDestroyed(this, entity);
169  }
170 
171  if(entity.retainCount == 1) {
172  // Can be released immediately without
173  // adding to _retainedEntities
174  entity.OnEntityReleased -= _cachedEntityReleased;
175  _reusableEntities.Push(entity);
176  entity.Release(this);
177  entity.RemoveAllOnEntityReleasedHandlers();
178  } else {
179  _retainedEntities.Add(entity);
180  entity.Release(this);
181  }
182  }
183 
184  /// Destroys all entities in the context.
185  /// Throws an exception if there are still retained entities.
186  public void DestroyAllEntities() {
187  var entities = GetEntities();
188  for(int i = 0; i < entities.Length; i++) {
189  DestroyEntity(entities[i]);
190  }
191 
192  _entities.Clear();
193 
194  if(_retainedEntities.Count != 0) {
196  }
197  }
198 
199  /// Determines whether the context has the specified entity.
200  public bool HasEntity(TEntity entity) {
201  return _entities.Contains(entity);
202  }
203 
204  /// Returns all entities which are currently in the context.
205  public TEntity[] GetEntities() {
206  if(_entitiesCache == null) {
207  _entitiesCache = new TEntity[_entities.Count];
208  _entities.CopyTo(_entitiesCache);
209  }
210 
211  return _entitiesCache;
212  }
213 
214  /// Returns a group for the specified matcher.
215  /// Calling context.GetGroup(matcher) with the same matcher will always
216  /// return the same instance of the group.
218  IGroup<TEntity> group;
219  if(!_groups.TryGetValue(matcher, out group)) {
220  group = new Group<TEntity>(matcher);
221  var entities = GetEntities();
222  for(int i = 0; i < entities.Length; i++) {
223  group.HandleEntitySilently(entities[i]);
224  }
225  _groups.Add(matcher, group);
226 
227  for(int i = 0; i < matcher.indices.Length; i++) {
228  var index = matcher.indices[i];
229  if(_groupsForIndex[index] == null) {
230  _groupsForIndex[index] = new List<IGroup<TEntity>>();
231  }
232  _groupsForIndex[index].Add(group);
233  }
234 
235  if(OnGroupCreated != null) {
236  OnGroupCreated(this, group);
237  }
238  }
239 
240  return group;
241  }
242 
243  /// Clears all groups. This is useful when you want to
244  /// soft-restart your application.
245  public void ClearGroups() {
246  foreach(var group in _groups.Values) {
247  group.RemoveAllEventHandlers();
248  var entities = group.GetEntities();
249  for(int i = 0; i < entities.Length; i++) {
250  entities[i].Release(group);
251  }
252 
253  if(OnGroupCleared != null) {
254  OnGroupCleared(this, group);
255  }
256  }
257  _groups.Clear();
258 
259  for(int i = 0; i < _groupsForIndex.Length; i++) {
260  _groupsForIndex[i] = null;
261  }
262  }
263 
264  /// Adds the IEntityIndex for the specified name.
265  /// There can only be one IEntityIndex per name.
266  public void AddEntityIndex(IEntityIndex entityIndex) {
267  if(_entityIndices.ContainsKey(entityIndex.name)) {
268  throw new ContextEntityIndexDoesAlreadyExistException(this, entityIndex.name);
269  }
270 
271  _entityIndices.Add(entityIndex.name, entityIndex);
272  }
273 
274  /// Gets the IEntityIndex for the specified name.
275  public IEntityIndex GetEntityIndex(string name) {
276  IEntityIndex entityIndex;
277  if(!_entityIndices.TryGetValue(name, out entityIndex)) {
278  throw new ContextEntityIndexDoesNotExistException(this, name);
279  }
280 
281  return entityIndex;
282  }
283 
284  /// Deactivates and removes all entity indices.
286  foreach(var entityIndex in _entityIndices.Values) {
287  entityIndex.Deactivate();
288  }
289 
290  _entityIndices.Clear();
291  }
292 
293  /// Resets the creationIndex back to 0.
294  public void ResetCreationIndex() {
295  _creationIndex = 0;
296  }
297 
298  /// Clears the componentPool at the specified index.
299  public void ClearComponentPool(int index) {
300  var componentPool = _componentPools[index];
301  if(componentPool != null) {
302  componentPool.Clear();
303  }
304  }
305 
306  /// Clears all componentPools.
307  public void ClearComponentPools() {
308  for(int i = 0; i < _componentPools.Length; i++) {
310  }
311  }
312 
313  /// Resets the context (destroys all entities and
314  /// resets creationIndex back to 0).
315  public void Reset() {
318 
319  OnEntityCreated = null;
320  OnEntityWillBeDestroyed = null;
321  OnEntityDestroyed = null;
322  OnGroupCreated = null;
323  OnGroupCleared = null;
324  }
325 
326  public override string ToString() {
327  return _contextInfo.name;
328  }
329 
330  void updateGroupsComponentAddedOrRemoved(IEntity entity, int index, IComponent component) {
331  var groups = _groupsForIndex[index];
332  if(groups != null) {
333  var events = _groupChangedListPool.Get();
334 
335  var tEntity = (TEntity)entity;
336 
337  for(int i = 0; i < groups.Count; i++) {
338  events.Add(groups[i].HandleEntity(tEntity));
339  }
340 
341  for(int i = 0; i < events.Count; i++) {
342  var groupChangedEvent = events[i];
343  if(groupChangedEvent != null) {
344  groupChangedEvent(
345  groups[i], tEntity, index, component
346  );
347  }
348  }
349 
350  _groupChangedListPool.Push(events);
351  }
352  }
353 
354  void updateGroupsComponentReplaced(IEntity entity, int index, IComponent previousComponent, IComponent newComponent) {
355  var groups = _groupsForIndex[index];
356  if(groups != null) {
357 
358  var tEntity = (TEntity)entity;
359 
360  for(int i = 0; i < groups.Count; i++) {
361  groups[i].UpdateEntity(
362  tEntity, index, previousComponent, newComponent
363  );
364  }
365  }
366  }
367 
368  void onEntityReleased(IEntity entity) {
369  if(entity.isEnabled) {
371  "Cannot release " + entity + "!"
372  );
373  }
374  var tEntity = (TEntity)entity;
375  entity.RemoveAllOnEntityReleasedHandlers();
376  _retainedEntities.Remove(tEntity);
377  _reusableEntities.Push(tEntity);
378  }
379  }
380 }
void ClearGroups()
Definition: Context.cs:245
bool HasEntity(TEntity entity)
Determines whether the context has the specified entity.
Definition: Context.cs:200
int totalComponents
Definition: Context.cs:29
ContextInfo contextInfo
Definition: Context.cs:40
void ClearComponentPool(int index)
Clears the componentPool at the specified index.
Definition: Context.cs:299
ContextGroupChanged OnGroupCreated
Occurs when a group gets created for the first time.
Definition: Context.cs:21
void Reset()
Definition: Context.cs:315
IEntityIndex GetEntityIndex(string name)
Gets the IEntityIndex for the specified name.
Definition: Context.cs:275
Context(int totalComponents, int startCreationIndex, ContextInfo contextInfo)
Definition: Context.cs:84
ContextEntityChanged OnEntityWillBeDestroyed
Occurs when an entity will be destroyed.
Definition: Context.cs:15
int count
Returns the number of entities in the context.
Definition: Context.cs:43
void ResetCreationIndex()
Resets the creationIndex back to 0.
Definition: Context.cs:294
void AddEntityIndex(IEntityIndex entityIndex)
Definition: Context.cs:266
TEntity [] GetEntities()
Returns all entities which are currently in the context.
Definition: Context.cs:205
int reusableEntitiesCount
Definition: Context.cs:47
int retainedEntitiesCount
Definition: Context.cs:51
void DeactivateAndRemoveEntityIndices()
Deactivates and removes all entity indices.
Definition: Context.cs:285
IGroup< TEntity > GetGroup(IMatcher< TEntity > matcher)
Definition: Context.cs:217
void DestroyEntity(TEntity entity)
Definition: Context.cs:151
TEntity CreateEntity()
Definition: Context.cs:123
void ClearComponentPools()
Clears all componentPools.
Definition: Context.cs:307
ContextEntityChanged OnEntityCreated
Occurs when an entity gets created.
Definition: Context.cs:12
Stack< IComponent > [] componentPools
Definition: Context.cs:36
ContextEntityChanged OnEntityDestroyed
Occurs when an entity got destroyed.
Definition: Context.cs:18
Context(int totalComponents)
Definition: Context.cs:79
void DestroyAllEntities()
Definition: Context.cs:186
ContextGroupChanged OnGroupCleared
Occurs when a group gets cleared.
Definition: Context.cs:24