Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
EntitySystem.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections;
5 using System.Collections.Generic;
6 using System.Collections.Specialized;
7 using System.Linq;
8 using System.Reflection;
9 
10 using SiliconStudio.Core;
11 using SiliconStudio.Core.Collections;
12 using SiliconStudio.Core.Diagnostics;
13 using SiliconStudio.Core.ReferenceCounting;
14 using SiliconStudio.Paradox.Engine;
15 using SiliconStudio.Paradox.Games;
16 
17 namespace SiliconStudio.Paradox.EntityModel
18 {
19  /// <summary>
20  /// Manage a collection of entities.
21  /// </summary>
22  public class EntitySystem : GameSystemBase, IReadOnlySet<Entity>
23  {
24  // TODO: Make this class threadsafe (current locks aren't sufficients)
25 
26  // List of all entities, with their respective processors
27  private readonly TrackingDictionary<Entity, List<EntityProcessor>> entities;
28 
29  // Enabled entities
30  private readonly TrackingHashSet<Entity> enabledEntities;
31 
32  private readonly TrackingHashSet<EntityProcessor> processors;
33 
34  public EntitySystem(IServiceRegistry registry)
35  : base(registry)
36  {
37  Services.AddService(typeof(EntitySystem), this);
38  Enabled = true;
39  Visible = true;
40 
41  entities = new TrackingDictionary<Entity, List<EntityProcessor>>();
42  enabledEntities = new TrackingHashSet<Entity>();
43 
44  processors = new TrackingHashSet<EntityProcessor>();
45  processors.CollectionChanged += new EventHandler<TrackingCollectionChangedEventArgs>(systems_CollectionChanged);
46  }
47 
48  /// <summary>
49  /// Gets the entity Processors.
50  /// </summary>
51  public ISet<EntityProcessor> Processors
52  {
53  get { return processors; }
54  }
55 
56  public override void Update(GameTime gameTime)
57  {
58  foreach (var processor in processors)
59  {
60  if (processor.Enabled)
61  {
62  using (Profiler.Begin(processor.UpdateProfilingKey))
63  {
64  processor.Update(gameTime);
65  }
66  }
67  }
68  }
69 
70  public override void Draw(GameTime gameTime)
71  {
72  foreach (var processor in processors)
73  {
74  if (processor.Enabled)
75  {
76  using (Profiler.Begin(processor.DrawProfilingKey))
77  {
78  processor.Draw(gameTime);
79  }
80  }
81  }
82  }
83 
84  /// <summary>
85  /// Adds the entity.
86  /// If the <see cref="Entity" /> has a parent, its parent should be added (or <see cref="TransformationComponent.Children" />) should be used.
87  /// </summary>
88  /// <param name="entity">The entity.</param>
89  /// <exception cref="System.ArgumentException">Entity shouldn't have a parent.;entity</exception>
90  public void Add(Entity entity)
91  {
92  // Entity can't be a root because it already has a parent?
93  if (entity.Transformation != null && entity.Transformation.Parent != null)
94  throw new ArgumentException("Entity shouldn't have a parent.", "entity");
95 
96  InternalAddEntity(entity);
97  }
98 
99  /// <summary>
100  /// Adds a collection of entities to this system.
101  /// </summary>
102  /// <param name="entitiesToAdd">The entities to add.</param>
103  public void Add(params Entity[] entitiesToAdd)
104  {
105  foreach (var entity in entitiesToAdd)
106  {
107  Add(entity);
108  }
109  }
110 
111  /// <summary>
112  /// Adds a collection of entities to this system.
113  /// </summary>
114  /// <param name="entitiesToAdd">The entities to add.</param>
115  public void AddRange(IEnumerable<Entity> entitiesToAdd)
116  {
117  foreach (var entity in entitiesToAdd)
118  {
119  Add(entity);
120  }
121  }
122 
123  /// <summary>
124  /// Sets the enable state of this entity.
125  /// </summary>
126  /// <param name="entity">The entity.</param>
127  /// <param name="enabled">if set to <c>true</c>, entity is [enabled].</param>
128  /// <exception cref="System.InvalidOperationException">Entity is not part of this EntityManager.</exception>
129  public void SetEnabled(Entity entity, bool enabled = true)
130  {
131  List<EntityProcessor> entityProcessors;
132  if (!entities.TryGetValue(entity, out entityProcessors))
133  throw new InvalidOperationException("Entity is not part of this EntityManager.");
134 
135  bool wasEnabled = enabledEntities.Contains(entity);
136 
137  if (enabled != wasEnabled)
138  {
139  if (enabled)
140  {
141  enabledEntities.Add(entity);
142  }
143  else
144  {
145  enabledEntities.Remove(entity);
146  }
147 
148  foreach (var component in entityProcessors)
149  {
150  component.SetEnabled(entity, enabled);
151  }
152  }
153  }
154 
155  /// <summary>
156  /// Determines whether the specified entity is enabled.
157  /// </summary>
158  /// <param name="entity">The entity.</param>
159  /// <returns><c>true</c> if the specified entity is enabled; otherwise, <c>false</c>.</returns>
160  /// <inheritdoc />
161  public bool IsEnabled(Entity entity)
162  {
163  return enabledEntities.Contains(entity);
164  }
165 
166  /// <summary>
167  /// Enables the specified entity.
168  /// </summary>
169  /// <param name="entity">The entity.</param>
170  /// <inheritdoc />
171  public void Enable(Entity entity)
172  {
173  SetEnabled(entity, true);
174  }
175 
176  /// <summary>
177  /// Disables the specified entity.
178  /// </summary>
179  /// <param name="entity">The entity.</param>
180  /// <inheritdoc />
181  public void Disable(Entity entity)
182  {
183  SetEnabled(entity, false);
184  }
185 
186  /// <summary>
187  /// Removes the entity from the <see cref="EntitySystem" />.
188  /// It works weither entity has a parent or not.
189  /// In conjonction with <see cref="HierarchicalSystem" />, it will remove children entities as well.
190  /// </summary>
191  /// <param name="entity">The entity.</param>
192  public void Remove(Entity entity)
193  {
194  InternalRemoveEntity(entity, true);
195  }
196 
197  /// <summary>
198  /// Removes all entities from the <see cref="EntitySystem"/>.
199  /// </summary>
200  public void Clear()
201  {
202  foreach (var entity in entities.Keys.ToList())
203  {
204  InternalRemoveEntity(entity, true);
205  }
206  }
207 
208  /// <summary>
209  /// Gets the processor.
210  /// </summary>
211  /// <typeparam name="T"></typeparam>
212  /// <returns>T.</returns>
213  public T GetProcessor<T>() where T : EntityProcessor
214  {
215  foreach (var system in processors)
216  {
217  if (system is T)
218  return (T)system;
219  }
220 
221  return null;
222  }
223 
224  /// <summary>
225  /// Adds the specified entity.
226  /// </summary>
227  /// <param name="entity">The entity to add.</param>
228  internal void InternalAddEntity(Entity entity)
229  {
230  // Already added?
231  if (entities.ContainsKey(entity))
232  return;
233 
234  var entityProcessors = new List<EntityProcessor>();
235  entities.Add(entity, entityProcessors);
236 
237  enabledEntities.Add(entity);
238 
239  entity.AddReferenceInternal();
240 
241  entity.Tags.PropertyUpdated += EntityPropertyUpdated;
242 
243  // Check which processor want this entity
244  foreach (var system in processors)
245  {
246  system.EntityCheck(entity, entityProcessors);
247  }
248  }
249 
250  /// <summary>
251  /// Removes the specified entity.
252  /// </summary>
253  /// <param name="entity">The entity to remove.</param>
254  /// <param name="removeParent">Indicate if entity should be removed from its parent</param>
255  internal void InternalRemoveEntity(Entity entity, bool removeParent)
256  {
257  // Entity wasn't already added
258  List<EntityProcessor> entityProcessors;
259  if (!entities.TryGetValue(entity, out entityProcessors))
260  return;
261 
262  entities.Remove(entity);
263  enabledEntities.Remove(entity);
264 
265  if (removeParent)
266  {
267  // Force parent to be null, so that it is removed even if it is not a root node
268  entity.Transformation.Parent = null;
269  }
270 
271  // Notify Processors thie entity has been removed
272  foreach (var system in processors)
273  {
274  system.EntityCheck(entity, entityProcessors, true);
275  }
276 
277  entity.Tags.PropertyUpdated -= EntityPropertyUpdated;
278 
279  entity.ReleaseInternal();
280  }
281 
282  private void AddSystem(EntityProcessor processor)
283  {
284  processor.EntitySystem = this;
285  processor.Services = Services;
286  processor.OnSystemAdd();
287  foreach (var entity in entities)
288  {
289  processor.EntityCheck(entity.Key, entity.Value);
290  }
291  }
292 
293  private void RemoveSystem(EntityProcessor processor)
294  {
295  processor.OnSystemRemove();
296  processor.Services = null;
297  processor.EntitySystem = null;
298  }
299 
300  private void EntityPropertyUpdated(ref PropertyContainer propertyContainer, PropertyKey propertyKey, object newValue, object oldValue)
301  {
302  // Only process EntityComponent properties
303  if (!typeof(EntityComponent).GetTypeInfo().IsAssignableFrom(propertyKey.PropertyType.GetTypeInfo()))
304  return;
305 
306  // No real update
307  if (oldValue == newValue)
308  return;
309 
310  var entity = (Entity)propertyContainer.Owner;
311  var entityProcessors = entities[entity];
312  foreach (EntityProcessor system in processors)
313  {
314  system.EntityCheck(entity, entityProcessors);
315  }
316  }
317 
318  //private void entities_CollectionChanged(object sender, TrackingCollectionChangedEventArgs e)
319  //{
320  // var entity = (Entity)e.Item;
321  // switch (e.Action)
322  // {
323  // case NotifyCollectionChangedAction.Add:
324  // InternalAddEntity(entity);
325  // break;
326  // case NotifyCollectionChangedAction.Remove:
327  // InternalRemoveEntity(entity);
328  // break;
329  // }
330  //}
331 
332  private void systems_CollectionChanged(object sender, TrackingCollectionChangedEventArgs e)
333  {
334  switch (e.Action)
335  {
336  case NotifyCollectionChangedAction.Add:
337  AddSystem((EntityProcessor)e.Item);
338  break;
339  case NotifyCollectionChangedAction.Remove:
340  RemoveSystem((EntityProcessor)e.Item);
341  break;
342  }
343  }
344 
345  /// <summary>
346  /// Determines whether this instance contains the specified entity.
347  /// </summary>
348  /// <param name="item">The item.</param>
349  /// <returns><c>true</c> if this instance contains the specified entity; otherwise, <c>false</c>.</returns>
350  public bool Contains(Entity item)
351  {
352  return entities.ContainsKey(item);
353  }
354 
355  public IEnumerator<Entity> GetEnumerator()
356  {
357  return entities.Keys.GetEnumerator();
358  }
359 
360  IEnumerator IEnumerable.GetEnumerator()
361  {
362  return GetEnumerator();
363  }
364 
365  public int Count
366  {
367  get
368  {
369  return entities.Count;
370  }
371  }
372  }
373 }
void Disable(Entity entity)
Disables the specified entity.
override void Update(GameTime gameTime)
This method is called when this game component is updated.
Definition: EntitySystem.cs:56
Game entity. It usually aggregates multiple EntityComponent
Definition: Entity.cs:28
void Clear()
Removes all entities from the EntitySystem.
void SetEnabled(Entity entity, bool enabled=true)
Sets the enable state of this entity.
void Enable(Entity entity)
Enables the specified entity.
Represents a container that can hold properties, lightweight to embed (lazy initialized).
TransformationComponent Transformation
Gets or sets the Transformation associated to this entity. Added for convenience over usual Get/Set m...
Definition: Entity.cs:74
A service registry is a IServiceProvider that provides methods to register and unregister services...
bool IsEnabled(Entity entity)
Determines whether the specified entity is enabled.
Entity processor, triggered on various EntitySystem events such as Entity and Component additions and...
Base class for a GameSystemBase component.
Current timing used for variable-step (real time) or fixed-step (game time) games.
Definition: GameTime.cs:31
Type PropertyType
Gets the type of the property.
Definition: PropertyKey.cs:116
object Item
Gets the added or removed item (if dictionary, value only).
Manage a collection of entities.
Definition: EntitySystem.cs:22
void Remove(Entity entity)
Removes the entity from the EntitySystem. It works weither entity has a parent or not...
NotifyCollectionChangedAction Action
Gets the type of action performed. Allowed values are NotifyCollectionChangedAction.Add and NotifyCollectionChangedAction.Remove.
override void Draw(GameTime gameTime)
Draws this instance.
Definition: EntitySystem.cs:70
TransformationComponent Parent
Gets or sets the parent of this TransformationComponent.
void Add(Entity entity)
Adds the entity. If the Entity has a parent, its parent should be added (or TransformationComponent.Children) should be used.
Definition: EntitySystem.cs:90
A class that represents a tag propety.
Definition: PropertyKey.cs:17
void AddRange(IEnumerable< Entity > entitiesToAdd)
Adds a collection of entities to this system.
bool Contains(Entity item)
Determines whether this instance contains the specified entity.
void Add(params Entity[] entitiesToAdd)
Adds a collection of entities to this system.