Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
GameBase.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.Reflection;
27 using SiliconStudio.Core.Diagnostics;
28 using SiliconStudio.Core.Mathematics;
29 using SiliconStudio.Paradox.Games.Time;
30 using SiliconStudio.Paradox.Graphics;
31 using SiliconStudio.Core;
32 using SiliconStudio.Core.Serialization.Assets;
33 
34 namespace SiliconStudio.Paradox.Games
35 {
36  /// <summary>
37  /// The game.
38  /// </summary>
39  public abstract class GameBase : ComponentBase, IGame, IVirtualResolution
40  {
41  #region Fields
42 
43  private readonly Dictionary<object, ProfilingKey> updateProfilers = new Dictionary<object, ProfilingKey>();
44  private readonly Dictionary<object, ProfilingKey> drawProfilers = new Dictionary<object, ProfilingKey>();
45  private readonly GameTime updateTime;
46  private readonly GameTime drawTime;
47  private readonly TimerTick playTimer;
48  private readonly TimerTick updateTimer;
49  private readonly int[] lastUpdateCount;
50  private readonly float updateCountAverageSlowLimit;
51  private readonly GamePlatform gamePlatform;
52  private ProfilingState profilingDraw;
53  private TimeSpan singleFrameUpdateTime;
54  private IGraphicsDeviceService graphicsDeviceService;
55  private IGraphicsDeviceManager graphicsDeviceManager;
56  private ResumeManager resumeManager;
57  private bool isEndRunRequired;
58  private bool isExiting;
59  private bool suppressDraw;
60 
61  private TimeSpan totalUpdateTime;
62  private TimeSpan totalDrawTime;
63  private readonly TimeSpan maximumElapsedTime;
64  private TimeSpan accumulatedElapsedGameTime;
65  private TimeSpan lastFrameElapsedGameTime;
66  private int nextLastUpdateCountIndex;
67  private bool drawRunningSlowly;
68  private bool forceElapsedTimeToZero;
69 
70  private readonly TimerTick timer;
71 
72  protected readonly ILogger Log;
73 
74  private bool isMouseVisible;
75 
76  internal bool SlowDownDrawCalls;
77 
78  private Vector3 virtualResolution;
79 
80  #endregion
81 
82  #region Constructors and Destructors
83 
84  /// <summary>
85  /// Initializes a new instance of the <see cref="GameBase" /> class.
86  /// </summary>
87  protected GameBase()
88  {
89  // Internals
90  Log = GlobalLogger.GetLogger(this.GetType().GetTypeInfo().Name);
91  updateTime = new GameTime();
92  drawTime = new GameTime();
93  playTimer = new TimerTick();
94  updateTimer = new TimerTick();
95  totalUpdateTime = new TimeSpan();
96  timer = new TimerTick();
97  IsFixedTimeStep = false;
98  maximumElapsedTime = TimeSpan.FromMilliseconds(500.0);
99  TargetElapsedTime = TimeSpan.FromTicks(10000000 / 60); // target elapsed time is by default 60Hz
100  lastUpdateCount = new int[4];
101  nextLastUpdateCountIndex = 0;
102 
103  // Calculate the updateCountAverageSlowLimit (assuming moving average is >=3 )
104  // Example for a moving average of 4:
105  // updateCountAverageSlowLimit = (2 * 2 + (4 - 2)) / 4 = 1.5f
106  const int BadUpdateCountTime = 2; // number of bad frame (a bad frame is a frame that has at least 2 updates)
107  var maxLastCount = 2 * Math.Min(BadUpdateCountTime, lastUpdateCount.Length);
108  updateCountAverageSlowLimit = (float)(maxLastCount + (lastUpdateCount.Length - maxLastCount)) / lastUpdateCount.Length;
109 
110  // Externals
111  Services = new ServiceRegistry();
112 
113  // Asset manager
114  Asset = new AssetManager();
115  Asset.Serializer.SerializerContextTags.Set(ServiceRegistry.ServiceRegistryKey, Services);
116 
117  LaunchParameters = new LaunchParameters();
118  GameSystems = new GameSystemCollection();
119 
120  // Create Platform
121  gamePlatform = GamePlatform.Create(this);
122  gamePlatform.Activated += gamePlatform_Activated;
123  gamePlatform.Deactivated += gamePlatform_Deactivated;
124  gamePlatform.Exiting += gamePlatform_Exiting;
125  gamePlatform.WindowCreated += GamePlatformOnWindowCreated;
126 
127  // Setup registry
128  Services.AddService(typeof(IGame), this);
129  Services.AddService(typeof(IVirtualResolution), this);
130  Services.AddService(typeof(IAssetManager), Asset);
131  Services.AddService(typeof(IGamePlatform), gamePlatform);
132 
133  IsActive = true;
134  }
135 
136  #endregion
137 
138  #region Public Events
139 
140  /// <summary>
141  /// Occurs when [activated].
142  /// </summary>
143  public event EventHandler<EventArgs> Activated;
144 
145  /// <summary>
146  /// Occurs when [deactivated].
147  /// </summary>
148  public event EventHandler<EventArgs> Deactivated;
149 
150  /// <summary>
151  /// Occurs when [exiting].
152  /// </summary>
153  public event EventHandler<EventArgs> Exiting;
154 
155  /// <summary>
156  /// Occurs when [window created].
157  /// </summary>
158  public event EventHandler<EventArgs> WindowCreated;
159 
160  public event EventHandler<GameUnhandledExceptionEventArgs> UnhandledException;
161 
162  #endregion
163 
164  #region Public Properties
165 
166  /// <summary>
167  /// Gets the current update time from the start of the game.
168  /// </summary>
169  /// <value>The current update time.</value>
170  public GameTime UpdateTime
171  {
172  get
173  {
174  return updateTime;
175  }
176  }
177 
178  /// <summary>
179  /// Gets the current draw time from the start of the game.
180  /// </summary>
181  /// <value>The current update time.</value>
182  public GameTime DrawTime
183  {
184  get
185  {
186  return drawTime;
187  }
188  }
189 
190  /// <summary>
191  /// Gets the draw interpolation factor, which is (<see cref="UpdateTime"/> - <see cref="DrawTime"/>) / <see cref="TargetElapsedTime"/>.
192  /// If <see cref="IsFixedTimeStep"/> is false, it will be 0 as <see cref="UpdateTime"/> and <see cref="DrawTime"/> will be equal.
193  /// </summary>
194  /// <value>
195  /// The draw interpolation factor.
196  /// </value>
197  public float DrawInterpolationFactor { get; private set; }
198 
199  /// <summary>
200  /// Gets the play time, can be changed to match to the time of the current rendering scene.
201  /// </summary>
202  /// <value>The play time.</value>
203  public TimerTick PlayTime
204  {
205  get
206  {
207  return playTimer;
208  }
209  }
210 
211  /// <summary>
212  /// Gets or sets the <see cref="AssetManager"/>.
213  /// </summary>
214  /// <value>The content manager.</value>
215  public AssetManager Asset { get; private set; }
216 
217  /// <summary>
218  /// Gets the game components registered by this game.
219  /// </summary>
220  /// <value>The game components.</value>
221  public GameSystemCollection GameSystems { get; private set; }
222 
223  /// <summary>
224  /// Gets the game context.
225  /// </summary>
226  /// <value>The game context.</value>
227  public GameContext Context { get; private set; }
228 
229  /// <summary>
230  /// Gets the graphics device.
231  /// </summary>
232  /// <value>The graphics device.</value>
234  {
235  get
236  {
237  if (graphicsDeviceService == null)
238  {
239  throw new InvalidOperationException("GraphicsDeviceService is not yet initialized");
240  }
241 
242  return graphicsDeviceService.GraphicsDevice;
243  }
244  }
245 
246  /// <summary>
247  /// Gets or sets the inactive sleep time.
248  /// </summary>
249  /// <value>The inactive sleep time.</value>
250  public TimeSpan InactiveSleepTime { get; set; }
251 
252  /// <summary>
253  /// Gets a value indicating whether this instance is active.
254  /// </summary>
255  /// <value><c>true</c> if this instance is active; otherwise, <c>false</c>.</value>
256  public bool IsActive { get; private set; }
257 
258  /// <summary>
259  /// Gets or sets a value indicating whether this instance is fixed time step.
260  /// </summary>
261  /// <value><c>true</c> if this instance is fixed time step; otherwise, <c>false</c>.</value>
262  public bool IsFixedTimeStep { get; set; }
263 
264  /// <summary>
265  /// Gets or sets a value indicating whether draw can happen as fast as possible, even when <see cref="IsFixedTimeStep"/> is set.
266  /// </summary>
267  /// <value><c>true</c> if this instance allows desychronized drawing; otherwise, <c>false</c>.</value>
268  public bool IsDrawDesynchronized { get; set; }
269 
270  public bool EarlyExit { get; set; }
271 
272  /// <summary>
273  /// Gets or sets a value indicating whether the mouse should be visible.
274  /// </summary>
275  /// <value><c>true</c> if the mouse should be visible; otherwise, <c>false</c>.</value>
276  public bool IsMouseVisible
277  {
278  get
279  {
280  return isMouseVisible;
281  }
282 
283  set
284  {
285  isMouseVisible = value;
286  if (Window != null)
287  {
288  Window.IsMouseVisible = value;
289  }
290  }
291  }
292 
293  /// <summary>
294  /// Gets the launch parameters.
295  /// </summary>
296  /// <value>The launch parameters.</value>
297  public LaunchParameters LaunchParameters { get; private set; }
298 
299  /// <summary>
300  /// Gets a value indicating whether is running.
301  /// </summary>
302  public bool IsRunning { get; private set; }
303 
304  /// <summary>
305  /// Gets the service container.
306  /// </summary>
307  /// <value>The service container.</value>
308  public ServiceRegistry Services { get; private set; }
309 
310  /// <summary>
311  /// Gets or sets the target elapsed time.
312  /// </summary>
313  /// <value>The target elapsed time.</value>
314  public TimeSpan TargetElapsedTime { get; set; }
315 
316  /// <summary>
317  /// Gets the abstract window.
318  /// </summary>
319  /// <value>The window.</value>
320  public GameWindow Window
321  {
322  get
323  {
324  if (gamePlatform != null)
325  {
326  return gamePlatform.MainWindow;
327  }
328  return null;
329  }
330  }
331 
332  public GameState State { get; set; }
333 
334  public Vector3 VirtualResolution
335  {
336  get { return virtualResolution; }
337  set
338  {
339  if(virtualResolution == value)
340  return;
341 
342  virtualResolution = value;
343 
344  var handler = VirtualResolutionChanged;
345  if (handler != null)
346  handler(this, EventArgs.Empty);
347  }
348  }
349 
350  public event EventHandler<EventArgs> VirtualResolutionChanged;
351 
352  #endregion
353 
354  internal EventHandler<GameUnhandledExceptionEventArgs> UnhandledExceptionInternal
355  {
356  get { return UnhandledException; }
357  }
358 
359  #region Public Methods and Operators
360 
361  /// <summary>
362  /// Exits the game.
363  /// </summary>
364  public void Exit()
365  {
366  isExiting = true;
367  gamePlatform.Exit();
368  }
369 
370  /// <summary>
371  /// Resets the elapsed time counter.
372  /// </summary>
373  public void ResetElapsedTime()
374  {
375  forceElapsedTimeToZero = true;
376  drawRunningSlowly = false;
377  Array.Clear(lastUpdateCount, 0, lastUpdateCount.Length);
378  nextLastUpdateCountIndex = 0;
379  }
380 
381  internal void InitializeBeforeRun()
382  {
383  try
384  {
385 
386  using (var profile = Profiler.Begin(GameProfilingKeys.GameInitialize))
387  {
388  // Make sure that the device is already created
389  graphicsDeviceManager.CreateDevice();
390 
391  // Gets the graphics device service
392  graphicsDeviceService = Services.GetService(typeof(IGraphicsDeviceService)) as IGraphicsDeviceService;
393  if (graphicsDeviceService == null)
394  {
395  throw new InvalidOperationException("No GraphicsDeviceService found");
396  }
397 
398  // Checks the graphics device
399  if (graphicsDeviceService.GraphicsDevice == null)
400  {
401  throw new InvalidOperationException("No GraphicsDevice found");
402  }
403 
404  // Initialize this instance and all game systems
405  Initialize();
406 
407  LoadContentInternal();
408 
409  IsRunning = true;
410 
411  BeginRun();
412 
413  timer.Reset();
414  updateTime.Reset(totalUpdateTime);
415 
416  // Run the first time an update
417  updateTimer.Reset();
418  using (Profiler.Begin(GameProfilingKeys.GameUpdate))
419  {
420  Update(updateTime);
421  }
422  updateTimer.Tick();
423  singleFrameUpdateTime += updateTimer.ElapsedTime;
424 
425  // Reset PlayTime
426  playTimer.Reset();
427  }
428  }
429  catch (Exception ex)
430  {
431  Log.Error("Unexpected exception", ex);
432  throw;
433  }
434  }
435 
436  /// <summary>
437  /// Call this method to initialize the game, begin running the game loop, and start processing events for the game.
438  /// </summary>
439  /// <param name="gameContext">The window Context for this game.</param>
440  /// <exception cref="System.InvalidOperationException">Cannot run this instance while it is already running</exception>
441  public void Run(GameContext gameContext = null)
442  {
443  if (IsRunning)
444  {
445  throw new InvalidOperationException("Cannot run this instance while it is already running");
446  }
447 
448  // Gets the graphics device manager
449  graphicsDeviceManager = Services.GetService(typeof(IGraphicsDeviceManager)) as IGraphicsDeviceManager;
450  if (graphicsDeviceManager == null)
451  {
452  throw new InvalidOperationException("No GraphicsDeviceManager found");
453  }
454 
455  // Gets the GameWindow Context
456  this.Context = gameContext ?? new GameContext();
457 
458  try
459  {
460  // TODO temporary workaround as the engine doesn't support yet resize
461  var graphicsDeviceManagerImpl = (GraphicsDeviceManager) graphicsDeviceManager;
462  Context.RequestedWidth = graphicsDeviceManagerImpl.PreferredBackBufferWidth;
463  Context.RequestedHeight = graphicsDeviceManagerImpl.PreferredBackBufferHeight;
464  Context.RequestedBackBufferFormat = graphicsDeviceManagerImpl.PreferredBackBufferFormat;
465  Context.RequestedDepthStencilFormat = graphicsDeviceManagerImpl.PreferredDepthStencilFormat;
466 
467  gamePlatform.Run(Context);
468 
469  if (gamePlatform.IsBlockingRun)
470  {
471  // If the previous call was blocking, then we can call Endrun
472  EndRun();
473  }
474  else
475  {
476  // EndRun will be executed on Game.Exit
477  isEndRunRequired = true;
478  }
479  }
480  finally
481  {
482  if (!isEndRunRequired)
483  {
484  IsRunning = false;
485  }
486  }
487  }
488 
489  /// <summary>
490  /// Prevents calls to Draw until the next Update.
491  /// </summary>
492  public void SuppressDraw()
493  {
494  suppressDraw = true;
495  }
496 
497  /// <summary>
498  /// Updates the game's clock and calls Update and Draw.
499  /// </summary>
500  public void Tick()
501  {
502  try
503  {
504 
505  // If this instance is existing, then don't make any further update/draw
506  if (isExiting)
507  {
508  CheckEndRun();
509  return;
510  }
511 
512  // If this instance is not active, sleep for an inactive sleep time
513  if (!IsActive)
514  {
515  Utilities.Sleep(InactiveSleepTime);
516  return;
517  }
518 
519  // Update the timer
520  timer.Tick();
521 
522  // Update the playTimer timer
523  playTimer.Tick();
524 
525  // Measure updateTimer
526  updateTimer.Reset();
527 
528  var elapsedAdjustedTime = timer.ElapsedTimeWithPause;
529 
530  if (forceElapsedTimeToZero)
531  {
532  elapsedAdjustedTime = TimeSpan.Zero;
533  forceElapsedTimeToZero = false;
534  }
535 
536  if (elapsedAdjustedTime > maximumElapsedTime)
537  {
538  elapsedAdjustedTime = maximumElapsedTime;
539  }
540 
541  bool suppressNextDraw = true;
542  int updateCount = 1;
543  var singleFrameElapsedTime = elapsedAdjustedTime;
544  var drawLag = 0L;
545 
546  if (IsFixedTimeStep)
547  {
548  // If the rounded TargetElapsedTime is equivalent to current ElapsedAdjustedTime
549  // then make ElapsedAdjustedTime = TargetElapsedTime. We take the same internal rules as XNA
550  if (Math.Abs(elapsedAdjustedTime.Ticks - TargetElapsedTime.Ticks) < (TargetElapsedTime.Ticks >> 6))
551  {
552  elapsedAdjustedTime = TargetElapsedTime;
553  }
554 
555  // Update the accumulated time
556  accumulatedElapsedGameTime += elapsedAdjustedTime;
557 
558  // Calculate the number of update to issue
559  updateCount = (int)(accumulatedElapsedGameTime.Ticks/TargetElapsedTime.Ticks);
560 
561  if (IsDrawDesynchronized)
562  {
563  drawLag = accumulatedElapsedGameTime.Ticks%TargetElapsedTime.Ticks;
564  suppressNextDraw = false;
565  }
566  else if (updateCount == 0)
567  {
568  // If there is no need for update, then exit
569  return;
570  }
571 
572  // Calculate a moving average on updateCount
573  lastUpdateCount[nextLastUpdateCountIndex] = updateCount;
574  float updateCountMean = 0;
575  for (int i = 0; i < lastUpdateCount.Length; i++)
576  {
577  updateCountMean += lastUpdateCount[i];
578  }
579 
580  updateCountMean /= lastUpdateCount.Length;
581  nextLastUpdateCountIndex = (nextLastUpdateCountIndex + 1)%lastUpdateCount.Length;
582 
583  // Test when we are running slowly
584  drawRunningSlowly = updateCountMean > updateCountAverageSlowLimit;
585 
586  // We are going to call Update updateCount times, so we can substract this from accumulated elapsed game time
587  accumulatedElapsedGameTime = new TimeSpan(accumulatedElapsedGameTime.Ticks - (updateCount*TargetElapsedTime.Ticks));
588  singleFrameElapsedTime = TargetElapsedTime;
589  }
590  else
591  {
592  Array.Clear(lastUpdateCount, 0, lastUpdateCount.Length);
593  nextLastUpdateCountIndex = 0;
594  drawRunningSlowly = false;
595  }
596 
597  bool beginDrawSuccessful = false;
598  try
599  {
600  beginDrawSuccessful = BeginDraw();
601 
602  // Reset the time of the next frame
603  for (lastFrameElapsedGameTime = TimeSpan.Zero; updateCount > 0 && !isExiting; updateCount--)
604  {
605  updateTime.Update(totalUpdateTime, singleFrameElapsedTime, singleFrameUpdateTime, drawRunningSlowly, true);
606  try
607  {
608  UpdateAndProfile(updateTime);
609  if (EarlyExit)
610  return;
611 
612  // If there is no exception, then we can draw the frame
613  suppressNextDraw &= suppressDraw;
614  suppressDraw = false;
615  }
616  finally
617  {
618  lastFrameElapsedGameTime += singleFrameElapsedTime;
619  totalUpdateTime += singleFrameElapsedTime;
620  }
621  }
622 
623  // End measuring update time
624  updateTimer.Tick();
625  singleFrameUpdateTime += updateTimer.ElapsedTime;
626 
627  // Update game time just before calling draw
628  //updateTime.Update(totalUpdateTime, singleFrameElapsedTime, singleFrameUpdateTime, drawRunningSlowly, true);
629 
630  if (!suppressNextDraw)
631  {
632  totalDrawTime = TimeSpan.FromTicks(totalUpdateTime.Ticks + drawLag);
633  DrawInterpolationFactor = (float)drawLag/(float)TargetElapsedTime.Ticks;
634  DrawFrame();
635  }
636 
637  singleFrameUpdateTime = TimeSpan.Zero;
638  }
639  finally
640  {
641  if (beginDrawSuccessful)
642  {
643  using (Profiler.Begin(GameProfilingKeys.GameEndDraw))
644  {
645  EndDraw(true);
646  }
647  }
648 
649  CheckEndRun();
650  }
651  }
652  catch (Exception ex)
653  {
654  Log.Error("Unexpected exception", ex);
655  throw;
656  }
657  }
658 
659  private void CheckEndRun()
660  {
661  if (isExiting && IsRunning && isEndRunRequired)
662  {
663  EndRun();
664  IsRunning = false;
665  }
666  }
667 
668  #endregion
669 
670  #region Methods
671 
672  /// <summary>
673  /// Starts the drawing of a frame. This method is followed by calls to Draw and EndDraw.
674  /// </summary>
675  /// <returns><c>true</c> to continue drawing, false to not call <see cref="Draw"/> and <see cref="EndDraw"/></returns>
676  protected virtual bool BeginDraw()
677  {
678  if ((graphicsDeviceManager != null) && !graphicsDeviceManager.BeginDraw())
679  {
680  return false;
681  }
682 
683  return true;
684  }
685 
686  /// <summary>
687  /// Called after all components are initialized but before the first update in the game loop.
688  /// </summary>
689  protected virtual void BeginRun()
690  {
691  }
692 
693  protected override void Destroy()
694  {
695  lock (this)
696  {
697  if (Window != null && Window.IsActivated) // force the window to be in an correct state during destroy (Deactivated events are sometimes dropped on windows)
698  Window.OnPause();
699 
700  var array = new IGameSystemBase[GameSystems.Count];
701  this.GameSystems.CopyTo(array, 0);
702  for (int i = 0; i < array.Length; i++)
703  {
704  var disposable = array[i] as IDisposable;
705  if (disposable != null)
706  {
707  disposable.Dispose();
708  }
709  }
710 
711  var disposableGraphicsManager = graphicsDeviceManager as IDisposable;
712  if (disposableGraphicsManager != null)
713  {
714  disposableGraphicsManager.Dispose();
715  }
716 
717  DisposeGraphicsDeviceEvents();
718 
719  if (gamePlatform != null)
720  {
721  gamePlatform.Release();
722  }
723  }
724 
725  base.Destroy();
726  }
727 
728  /// <summary>
729  /// Reference page contains code sample.
730  /// </summary>
731  /// <param name="gameTime">
732  /// Time passed since the last call to Draw.
733  /// </param>
734  protected virtual void Draw(GameTime gameTime)
735  {
736  GameSystems.Draw(gameTime);
737 
738  // Make sure that the render target is set back to the back buffer
739  // From a user perspective this is better. From an internal point of view,
740  // this code is already present in GraphicsDeviceManager.BeginDraw()
741  // but due to the fact that a GameSystem can modify the state of GraphicsDevice
742  // we need to restore the default render targets
743  // TODO: Check how we can handle this more cleanly
744  if (GraphicsDevice != null && GraphicsDevice.BackBuffer != null)
745  {
746  GraphicsDevice.SetRenderTarget(GraphicsDevice.DepthStencilBuffer, GraphicsDevice.BackBuffer);
747  }
748  }
749 
750  /// <summary>Ends the drawing of a frame. This method is preceeded by calls to Draw and BeginDraw.</summary>
751  protected virtual void EndDraw(bool present)
752  {
753  if (graphicsDeviceManager != null)
754  {
755  graphicsDeviceManager.EndDraw(present);
756  }
757  }
758 
759  /// <summary>Called after the game loop has stopped running before exiting.</summary>
760  protected virtual void EndRun()
761  {
762  }
763 
764  /// <summary>Called after the Game and GraphicsDevice are created, but before LoadContent. Reference page contains code sample.</summary>
765  protected virtual void Initialize()
766  {
767  // Setup the graphics device if it was not already setup.
768  SetupGraphicsDeviceEvents();
769 
770  GameSystems.Initialize();
771  }
772 
773  internal virtual void LoadContentInternal()
774  {
775  GameSystems.LoadContent();
776  }
777 
778  internal bool IsExiting()
779  {
780  return isExiting;
781  }
782 
783  /// <summary>
784  /// Raises the Activated event. Override this method to add code to handle when the game gains focus.
785  /// </summary>
786  /// <param name="sender">The Game.</param>
787  /// <param name="args">Arguments for the Activated event.</param>
788  protected virtual void OnActivated(object sender, EventArgs args)
789  {
790  var handler = Activated;
791  if (handler != null)
792  {
793  handler(this, args);
794  }
795  }
796 
797  /// <summary>
798  /// Raises the Deactivated event. Override this method to add code to handle when the game loses focus.
799  /// </summary>
800  /// <param name="sender">The Game.</param>
801  /// <param name="args">Arguments for the Deactivated event.</param>
802  protected virtual void OnDeactivated(object sender, EventArgs args)
803  {
804  var handler = Deactivated;
805  if (handler != null)
806  {
807  handler(this, args);
808  }
809  }
810 
811  /// <summary>
812  /// Raises an Exiting event. Override this method to add code to handle when the game is exiting.
813  /// </summary>
814  /// <param name="sender">The Game.</param>
815  /// <param name="args">Arguments for the Exiting event.</param>
816  protected virtual void OnExiting(object sender, EventArgs args)
817  {
818  var handler = Exiting;
819  if (handler != null)
820  {
821  handler(this, args);
822  }
823  }
824 
825  protected virtual void OnWindowCreated()
826  {
827  EventHandler<EventArgs> handler = WindowCreated;
828  if (handler != null)
829  {
830  handler(this, EventArgs.Empty);
831  }
832  }
833 
834  private void GamePlatformOnWindowCreated(object sender, EventArgs eventArgs)
835  {
836  IsMouseVisible = true;
837  OnWindowCreated();
838  }
839 
840 
841  /// <summary>
842  /// This is used to display an error message if there is no suitable graphics device or sound card.
843  /// </summary>
844  /// <param name="exception">The exception to display.</param>
845  /// <returns>The <see cref="bool" />.</returns>
846  protected virtual bool ShowMissingRequirementMessage(Exception exception)
847  {
848  return true;
849  }
850 
851  /// <summary>
852  /// Called when graphics resources need to be unloaded. Override this method to unload any game-specific graphics resources.
853  /// </summary>
854  protected virtual void UnloadContent()
855  {
856  GameSystems.UnloadContent();
857  }
858 
859  /// <summary>
860  /// Reference page contains links to related conceptual articles.
861  /// </summary>
862  /// <param name="gameTime">
863  /// Time passed since the last call to Update.
864  /// </param>
865  protected virtual void Update(GameTime gameTime)
866  {
867  GameSystems.Update(gameTime);
868  }
869 
870  private void UpdateAndProfile(GameTime gameTime)
871  {
872  updateTimer.Reset();
873  using (Profiler.Begin(GameProfilingKeys.GameUpdate))
874  {
875  Update(gameTime);
876  }
877  updateTimer.Tick();
878  singleFrameUpdateTime += updateTimer.ElapsedTime;
879  }
880 
881  private void gamePlatform_Activated(object sender, EventArgs e)
882  {
883  if (!IsActive)
884  {
885  IsActive = true;
886  OnActivated(this, EventArgs.Empty);
887  }
888  }
889 
890  private void gamePlatform_Deactivated(object sender, EventArgs e)
891  {
892  if (IsActive)
893  {
894  IsActive = false;
895  OnDeactivated(this, EventArgs.Empty);
896  }
897  }
898 
899  private void gamePlatform_Exiting(object sender, EventArgs e)
900  {
901  OnExiting(this, EventArgs.Empty);
902  }
903 
904  private void DrawFrame()
905  {
906  if (SlowDownDrawCalls && (UpdateTime.FrameCount & 1) == 1) // skip the draw call about one frame over two.
907  return;
908 
909  try
910  {
911  // Initialized
912  if (!profilingDraw.IsInitialized)
913  {
914  profilingDraw = Profiler.Begin(GameProfilingKeys.GameDrawFPS);
915  }
916 
917  // Update profiling data
918  profilingDraw.CheckIfEnabled();
919 
920  if (!isExiting && GameSystems.IsFirstUpdateDone && !Window.IsMinimized)
921  {
922  drawTime.Update(totalDrawTime, lastFrameElapsedGameTime, singleFrameUpdateTime, drawRunningSlowly, true);
923 
924  if (drawTime.FramePerSecondUpdated)
925  {
926  // TODO: store some GameTime information as attributes in the Profiling using profilingDraw.SetAttribute(..)
927  profilingDraw.Mark("Frame = {0}, Update = {1:0.000}ms, Draw = {2:0.000}ms, FPS = {3:0.00}", drawTime.FrameCount, updateTime.TimePerFrame.TotalMilliseconds, drawTime.TimePerFrame.TotalMilliseconds, drawTime.FramePerSecond);
928  }
929 
930  using (Profiler.Begin(GameProfilingKeys.GameDraw))
931  {
932  Draw(drawTime);
933  }
934  }
935  }
936  finally
937  {
938  lastFrameElapsedGameTime = TimeSpan.Zero;
939  }
940  }
941 
942  private void SetupGraphicsDeviceEvents()
943  {
944  // Find the IGraphicsDeviceSerive.
945  graphicsDeviceService = Services.GetService(typeof(IGraphicsDeviceService)) as IGraphicsDeviceService;
946 
947  // If there is no graphics device service, don't go further as the whole Game would not work
948  if (graphicsDeviceService == null)
949  {
950  throw new InvalidOperationException("Unable to create a IGraphicsDeviceService");
951  }
952 
953  if (graphicsDeviceService.GraphicsDevice == null)
954  {
955  throw new InvalidOperationException("Unable to find a GraphicsDevice instance");
956  }
957 
958  resumeManager = new ResumeManager(Services);
959 
960  graphicsDeviceService.DeviceCreated += graphicsDeviceService_DeviceCreated;
961  graphicsDeviceService.DeviceResetting += graphicsDeviceService_DeviceResetting;
962  graphicsDeviceService.DeviceReset += graphicsDeviceService_DeviceReset;
963  graphicsDeviceService.DeviceDisposing += graphicsDeviceService_DeviceDisposing;
964  }
965 
966  private void DisposeGraphicsDeviceEvents()
967  {
968  if (graphicsDeviceService != null)
969  {
970  graphicsDeviceService.DeviceCreated -= graphicsDeviceService_DeviceCreated;
971  graphicsDeviceService.DeviceResetting -= graphicsDeviceService_DeviceResetting;
972  graphicsDeviceService.DeviceReset -= graphicsDeviceService_DeviceReset;
973  graphicsDeviceService.DeviceDisposing -= graphicsDeviceService_DeviceDisposing;
974  }
975  }
976 
977  private void graphicsDeviceService_DeviceCreated(object sender, EventArgs e)
978  {
979  if (GameSystems.State != GameSystemState.ContentLoaded)
980  {
981  LoadContentInternal();
982  }
983  }
984 
985  private void graphicsDeviceService_DeviceDisposing(object sender, EventArgs e)
986  {
987  // TODO: Unload all assets
988  //Asset.UnloadAll();
989 
990  if (GameSystems.State == GameSystemState.ContentLoaded)
991  {
992  UnloadContent();
993  }
994  }
995 
996  private void graphicsDeviceService_DeviceReset(object sender, EventArgs e)
997  {
998  // TODO: ResumeManager?
999  //throw new NotImplementedException();
1000  resumeManager.OnReload();
1001  resumeManager.OnRecreate();
1002  }
1003 
1004  private void graphicsDeviceService_DeviceResetting(object sender, EventArgs e)
1005  {
1006  // TODO: ResumeManager?
1007  //throw new NotImplementedException();
1008  resumeManager.OnDestroyed();
1009  }
1010 
1011  #endregion
1012  }
1013 }
Service providing method to access GraphicsDevice life-cycle.
A profiler state contains information of a portion of code being profiled. See remarks.
This provides timing information similar to System.Diagnostics.Stopwatch but an update occuring only ...
Definition: TimerTick.cs:31
GameBase()
Initializes a new instance of the GameBase class.
Definition: GameBase.cs:87
EventHandler< EventArgs > Deactivated
Occurs when [deactivated].
Definition: GameBase.cs:148
Interface providing services to deal with the virtual resolution of the game. The virtual resolution ...
virtual void OnDeactivated(object sender, EventArgs args)
Raises the Deactivated event. Override this method to add code to handle when the game loses focus...
Definition: GameBase.cs:802
void ResetElapsedTime()
Resets the elapsed time counter.
Definition: GameBase.cs:373
EventHandler< GameUnhandledExceptionEventArgs > UnhandledException
Definition: GameBase.cs:160
void SuppressDraw()
Prevents calls to Draw until the next Update.
Definition: GameBase.cs:492
void Run(GameContext gameContext=null)
Call this method to initialize the game, begin running the game loop, and start processing events for...
Definition: GameBase.cs:441
virtual void BeginRun()
Called after all components are initialized but before the first update in the game loop...
Definition: GameBase.cs:689
RenderTarget BackBuffer
Gets the back buffer sets by the current Presenter setup on this device.
EventHandler< EventArgs > WindowCreated
Occurs when [window created].
Definition: GameBase.cs:158
Interface for a game platform (OS, machine dependent).
Definition: IGamePlatform.cs:8
void Tick()
Updates the game's clock and calls Update and Draw.
Definition: GameBase.cs:500
Represents a three dimensional mathematical vector.
Definition: Vector3.cs:42
Base class for a framework component.
virtual bool ShowMissingRequirementMessage(Exception exception)
This is used to display an error message if there is no suitable graphics device or sound card...
Definition: GameBase.cs:846
Defines the interface for an object that manages a GraphicsDevice.
Defines a generic game system.
Performs primitive-based rendering, creates resources, handles system-level variables, adjusts gamma ramp levels, and creates shaders. See The+GraphicsDevice+class to learn more about the class.
virtual void OnActivated(object sender, EventArgs args)
Raises the Activated event. Override this method to add code to handle when the game gains focus...
Definition: GameBase.cs:788
EventHandler< EventArgs > VirtualResolutionChanged
Definition: GameBase.cs:350
override void Destroy()
Disposes of object resources.
Definition: GameBase.cs:693
virtual bool BeginDraw()
Starts the drawing of a frame. This method is followed by calls to Draw and EndDraw.
Definition: GameBase.cs:676
Current timing used for variable-step (real time) or fixed-step (game time) games.
Definition: GameTime.cs:31
virtual void UnloadContent()
Called when graphics resources need to be unloaded. Override this method to unload any game-specific ...
Definition: GameBase.cs:854
virtual void Draw(GameTime gameTime)
Reference page contains code sample.
Definition: GameBase.cs:734
virtual void EndRun()
Called after the game loop has stopped running before exiting.
Definition: GameBase.cs:760
Contains context used to render the game (Control for WinForm, a DrawingSurface for WP8...
Definition: GameContext.cs:31
Parameters used when launching an application.
EventHandler< EventArgs > Exiting
Occurs when [exiting].
Definition: GameBase.cs:153
Base implementation for IServiceRegistry
GameSystemState
Describes state of the GameSystemCollection.
virtual void Update(GameTime gameTime)
Reference page contains links to related conceptual articles.
Definition: GameBase.cs:865
virtual void OnExiting(object sender, EventArgs args)
Raises an Exiting event. Override this method to add code to handle when the game is exiting...
Definition: GameBase.cs:816
void Exit()
Exits the game.
Definition: GameBase.cs:364
Output message to log right away.
Interface for logging.
Definition: ILogger.cs:8
virtual void EndDraw(bool present)
Ends the drawing of a frame. This method is preceeded by calls to Draw and BeginDraw.
Definition: GameBase.cs:751
EventHandler< EventArgs > Activated
Occurs when [activated].
Definition: GameBase.cs:143
virtual void Initialize()
Called after the Game and GraphicsDevice are created, but before LoadContent. Reference page contains...
Definition: GameBase.cs:765