Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
GameSystemCollection.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 //
4 // Copyright (c) 2010-2013 SharpDX - Alexandre Mutel
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights
9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 // copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 // THE SOFTWARE.
23 
24 using System;
25 using System.Collections.Generic;
26 using System.Collections.Specialized;
27 using SiliconStudio.Core.Collections;
28 using SiliconStudio.Core.Diagnostics;
29 
30 namespace SiliconStudio.Paradox.Games
31 {
32  /// <summary>A collection of game components.</summary>
33  public class GameSystemCollection : TrackingCollection<IGameSystemBase>
34  {
35  private readonly List<IGameSystemBase> pendingGameSystems;
36  private readonly List<KeyValuePair<IDrawable, ProfilingKey>> drawableGameSystems;
37  private readonly List<KeyValuePair<IUpdateable, ProfilingKey>> updateableGameSystems;
38  private readonly List<IContentable> contentableGameSystems;
39  private readonly List<IContentable> currentlyContentGameSystems;
40  private readonly List<KeyValuePair<IDrawable, ProfilingKey>> currentlyDrawingGameSystems;
41  private readonly List<KeyValuePair<IUpdateable, ProfilingKey>> currentlyUpdatingGameSystems;
42  private bool isFirstUpdateDone = false;
43 
45  {
46  drawableGameSystems = new List<KeyValuePair<IDrawable, ProfilingKey>>();
47  currentlyContentGameSystems = new List<IContentable>();
48  currentlyDrawingGameSystems = new List<KeyValuePair<IDrawable, ProfilingKey>>();
49  pendingGameSystems = new List<IGameSystemBase>();
50  updateableGameSystems = new List<KeyValuePair<IUpdateable, ProfilingKey>>();
51  currentlyUpdatingGameSystems = new List<KeyValuePair<IUpdateable, ProfilingKey>>();
52  contentableGameSystems = new List<IContentable>();
53 
54  // Register events on GameSystems.
55  CollectionChanged += GameSystems_CollectionChanged;
56  }
57 
58  /// <summary>
59  /// Gets the state of this game system collection.
60  /// </summary>
61  /// <value>
62  /// The state of this game system collection.
63  /// </value>
64  public GameSystemState State { get; private set; }
65 
66  /// <summary>
67  /// Gets a value indicating whether first update has been done.
68  /// </summary>
69  /// <value>
70  /// <c>true</c> if first update has been done; otherwise, <c>false</c>.
71  /// </value>
72  public bool IsFirstUpdateDone { get { return isFirstUpdateDone; } }
73 
74 
75  /// <summary>
76  /// Reference page contains links to related conceptual articles.
77  /// </summary>
78  /// <param name="gameTime">
79  /// Time passed since the last call to Update.
80  /// </param>
81  public virtual void Update(GameTime gameTime)
82  {
83  lock (updateableGameSystems)
84  {
85  foreach (var updateable in updateableGameSystems)
86  {
87  currentlyUpdatingGameSystems.Add(updateable);
88  }
89  }
90 
91  foreach (var updateable in currentlyUpdatingGameSystems)
92  {
93  if (updateable.Key.Enabled)
94  {
95  using (Profiler.Begin(updateable.Value))
96  {
97  updateable.Key.Update(gameTime);
98  }
99  }
100  }
101 
102  currentlyUpdatingGameSystems.Clear();
103  isFirstUpdateDone = true;
104  }
105 
106  /// <summary>
107  /// Reference page contains code sample.
108  /// </summary>
109  /// <param name="gameTime">
110  /// Time passed since the last call to Draw.
111  /// </param>
112  public virtual void Draw(GameTime gameTime)
113  {
114  // Just lock current drawable game systems to grab them in a temporary list.
115  lock (drawableGameSystems)
116  {
117  for (int i = 0; i < drawableGameSystems.Count; i++)
118  {
119  currentlyDrawingGameSystems.Add(drawableGameSystems[i]);
120  }
121  }
122 
123  for (int i = 0; i < currentlyDrawingGameSystems.Count; i++)
124  {
125  var drawable = currentlyDrawingGameSystems[i];
126  if (drawable.Key.Visible)
127  {
128  using (Profiler.Begin(drawable.Value))
129  {
130  if (drawable.Key.BeginDraw())
131  {
132  drawable.Key.Draw(gameTime);
133  drawable.Key.EndDraw();
134  }
135  }
136  }
137  }
138 
139  currentlyDrawingGameSystems.Clear();
140  }
141 
142  /// <summary>
143  /// Loads the content.
144  /// </summary>
145  public virtual void LoadContent()
146  {
147  if (State != GameSystemState.Initialized)
148  {
149  throw new InvalidOperationException("Not initialized.");
150  }
151 
152  State = GameSystemState.ContentLoaded;
153 
154  lock (contentableGameSystems)
155  {
156  foreach (var contentable in contentableGameSystems)
157  {
158  currentlyContentGameSystems.Add(contentable);
159  }
160  }
161 
162  foreach (var contentable in currentlyContentGameSystems)
163  {
164  using (var profile = Profiler.Begin(GameProfilingKeys.GameSystemLoadContent, GetGameSystemName(contentable)))
165  contentable.LoadContent();
166  }
167 
168  currentlyContentGameSystems.Clear();
169  }
170 
171  /// <summary>
172  /// Called when graphics resources need to be unloaded. Override this method to unload any game-specific graphics resources.
173  /// </summary>
174  public virtual void UnloadContent()
175  {
176  if (State != GameSystemState.ContentLoaded)
177  {
178  throw new InvalidOperationException("Not running.");
179  }
180 
181  State = GameSystemState.Initialized;
182 
183  lock (contentableGameSystems)
184  {
185  foreach (var contentable in contentableGameSystems)
186  {
187  currentlyContentGameSystems.Add(contentable);
188  }
189  }
190 
191  foreach (var contentable in currentlyContentGameSystems)
192  {
193  contentable.UnloadContent();
194  }
195 
196  currentlyContentGameSystems.Clear();
197  }
198 
199  public void Initialize()
200  {
201  if (State != GameSystemState.None)
202  {
203  throw new InvalidOperationException("Already initialized.");
204  }
205 
206  State = GameSystemState.Initialized;
207 
208  InitializePendingGameSystems();
209  }
210 
211  private void InitializePendingGameSystems()
212  {
213  // Add all game systems that were added to this game instance before the game started.
214  while (pendingGameSystems.Count != 0)
215  {
216  var gameSystemName = GetGameSystemName(pendingGameSystems[0]);
217  using (var profile = Profiler.Begin(GameProfilingKeys.GameSystemInitialize, gameSystemName))
218  pendingGameSystems[0].Initialize();
219  if (State == GameSystemState.ContentLoaded && pendingGameSystems[0] is IContentable)
220  {
221  var contentable = (IContentable)pendingGameSystems[0];
222  using (var profile = Profiler.Begin(GameProfilingKeys.GameSystemLoadContent, gameSystemName))
223  contentable.LoadContent();
224  }
225 
226  pendingGameSystems.RemoveAt(0);
227  }
228  }
229 
230 
231  void GameSystems_CollectionChanged(object sender, TrackingCollectionChangedEventArgs e)
232  {
233  if (e.Action == NotifyCollectionChangedAction.Add)
234  {
235  GameSystems_ItemAdded(sender, e);
236  }
237  else if (e.Action == NotifyCollectionChangedAction.Remove)
238  {
239  GameSystems_ItemRemoved(sender, e);
240  }
241  }
242 
243  private void GameSystems_ItemAdded(object sender, TrackingCollectionChangedEventArgs e)
244  {
245  var gameSystem = (IGameSystemBase)e.Item;
246 
247  // If the game is already running, then we can initialize the game system now
248  if (State >= GameSystemState.Initialized)
249  {
250  gameSystem.Initialize();
251  }
252  else
253  {
254  // else we need to initialize it later
255  pendingGameSystems.Add(gameSystem);
256  }
257 
258  // Add a contentable system to a separate list
259  var contentableSystem = gameSystem as IContentable;
260  if (contentableSystem != null)
261  {
262  lock (contentableGameSystems)
263  {
264  if (!contentableGameSystems.Contains(contentableSystem))
265  {
266  contentableGameSystems.Add(contentableSystem);
267  }
268  }
269  // Load the content of IContentable when running
270  if (State >= GameSystemState.ContentLoaded)
271  {
272  contentableSystem.LoadContent();
273  }
274  }
275 
276  // Add an updateable system to the separate list
277  var updateableGameSystem = gameSystem as IUpdateable;
278  if (updateableGameSystem != null && AddGameSystem(updateableGameSystem, updateableGameSystems, UpdateableComparer.Default, GameProfilingKeys.GameUpdate))
279  {
280  updateableGameSystem.UpdateOrderChanged += updateableGameSystem_UpdateOrderChanged;
281  }
282 
283  // Add a drawable system to the separate list
284  var drawableGameSystem = gameSystem as IDrawable;
285  if (drawableGameSystem != null && AddGameSystem(drawableGameSystem, drawableGameSystems, DrawableComparer.Default, GameProfilingKeys.GameDraw))
286  {
287  drawableGameSystem.DrawOrderChanged += drawableGameSystem_DrawOrderChanged;
288  }
289  }
290 
291  private void GameSystems_ItemRemoved(object sender, TrackingCollectionChangedEventArgs e)
292  {
293  var gameSystem = (IGameSystemBase)e.Item;
294 
295  if (State == GameSystemState.None)
296  {
297  pendingGameSystems.Remove(gameSystem);
298  }
299 
300  var contentableSystem = gameSystem as IContentable;
301  if (contentableSystem != null)
302  {
303  lock (contentableGameSystems)
304  {
305  contentableGameSystems.Remove(contentableSystem);
306  }
307 
308  // UnLoads the content of IContentable when running
309  if (State == GameSystemState.ContentLoaded)
310  {
311  contentableSystem.UnloadContent();
312  }
313  }
314 
315  var updateableSystem = gameSystem as IUpdateable;
316  if (updateableSystem != null)
317  {
318  lock (updateableGameSystems)
319  {
320  var key = new KeyValuePair<IUpdateable, ProfilingKey>(updateableSystem, null);
321  updateableGameSystems.Remove(key);
322  }
323 
324  updateableSystem.UpdateOrderChanged -= updateableGameSystem_UpdateOrderChanged;
325  }
326 
327  var drawableSystem = gameSystem as IDrawable;
328  if (drawableSystem != null)
329  {
330  lock (drawableGameSystems)
331  {
332  var key = new KeyValuePair<IDrawable, ProfilingKey>(drawableSystem, null);
333  drawableGameSystems.Remove(key);
334  }
335 
336  drawableSystem.DrawOrderChanged -= drawableGameSystem_DrawOrderChanged;
337  }
338  }
339 
340  private void updateableGameSystem_UpdateOrderChanged(object sender, EventArgs e)
341  {
342  AddGameSystem((IUpdateable)sender, updateableGameSystems, UpdateableComparer.Default, GameProfilingKeys.GameUpdate, true);
343  }
344 
345  private void drawableGameSystem_DrawOrderChanged(object sender, EventArgs e)
346  {
347  AddGameSystem((IDrawable)sender, drawableGameSystems, DrawableComparer.Default, GameProfilingKeys.GameDraw, true);
348  }
349 
350  private static bool AddGameSystem<T>(T gameSystem, List<KeyValuePair<T, ProfilingKey>> gameSystems, IComparer<KeyValuePair<T, ProfilingKey>> comparer, ProfilingKey parentProfilingKey, bool removePreviousSystem = false)
351  where T : class
352  {
353  lock (gameSystems)
354  {
355  var gameSystemKey = new KeyValuePair<T, ProfilingKey>(gameSystem, null);
356 
357  // Find this gameSystem
358  int index = -1;
359  for (int i = 0; i < gameSystems.Count; ++i)
360  {
361  if (gameSystem == gameSystems[i].Key)
362  {
363  index = i;
364  }
365  }
366 
367  // If we are updating the order
368  if (index >= 0)
369  {
370  if (removePreviousSystem)
371  {
372  gameSystemKey = gameSystems[index];
373  gameSystems.RemoveAt(index);
374  index = -1;
375  }
376  }
377  else
378  {
379  gameSystemKey = new KeyValuePair<T, ProfilingKey>(gameSystemKey.Key, new ProfilingKey(parentProfilingKey, gameSystem.GetType().Name));
380  }
381 
382  if (index == -1)
383  {
384  // we want to insert right after all other systems with same draw/update order
385  index = gameSystems.UpperBound(gameSystemKey, comparer, 0, gameSystems.Count);
386 
387  gameSystems.Insert(index, gameSystemKey);
388 
389  // True, the system was inserted
390  return true;
391  }
392  }
393 
394  // False, it is already in the list
395  return false;
396  }
397 
398  private static string GetGameSystemName(object gameSystem)
399  {
400  return gameSystem is IGameSystemBase ? ((IGameSystemBase)gameSystem).Name : gameSystem != null ? gameSystem.GetType().Name : "null";
401  }
402 
403  /// <summary>
404  /// The comparer used to order <see cref="IDrawable"/> objects.
405  /// </summary>
406  internal struct DrawableComparer : IComparer<KeyValuePair<IDrawable, ProfilingKey>>
407  {
408  public static readonly DrawableComparer Default = new DrawableComparer();
409 
410  public int Compare(KeyValuePair<IDrawable, ProfilingKey> leftValue, KeyValuePair<IDrawable, ProfilingKey> rightValue)
411  {
412  var left = leftValue.Key;
413  var right = rightValue.Key;
414 
415  return left.DrawOrder.CompareTo(right.DrawOrder);
416  }
417  }
418 
419  /// <summary>
420  /// The comparer used to order <see cref="IUpdateable"/> objects.
421  /// </summary>
422  internal struct UpdateableComparer : IComparer<KeyValuePair<IUpdateable, ProfilingKey>>
423  {
424  public static readonly UpdateableComparer Default = new UpdateableComparer();
425 
426  public int Compare(KeyValuePair<IUpdateable, ProfilingKey> leftValue, KeyValuePair<IUpdateable, ProfilingKey> rightValue)
427  {
428  var left = leftValue.Key;
429  var right = rightValue.Key;
430 
431  return left.UpdateOrder.CompareTo(right.UpdateOrder);
432  }
433  }
434  }
435 }
virtual void Update(GameTime gameTime)
Reference page contains links to related conceptual articles.
virtual void Draw(GameTime gameTime)
Reference page contains code sample.
virtual void UnloadContent()
Called when graphics resources need to be unloaded. Override this method to unload any game-specific ...
A key to identify a specific profile.
Definition: ProfilingKey.cs:11
An interface to load and unload asset.
Definition: IContentable.cs:29
Use the default mode depending on the type of the field/property.
Current timing used for variable-step (real time) or fixed-step (game time) games.
Definition: GameTime.cs:31
object Item
Gets the added or removed item (if dictionary, value only).
NotifyCollectionChangedAction Action
Gets the type of action performed. Allowed values are NotifyCollectionChangedAction.Add and NotifyCollectionChangedAction.Remove.
GameSystemState
Describes state of the GameSystemCollection.