2 * Copyright (c) 2007-2009 SlimDX Group
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 using System.ComponentModel;
24 using System.Threading;
25 using System.Windows.Forms;
27 using SharpDX.Direct3D9;
28 using System.Collections.ObjectModel;
30 namespace SampleFramework
33 /// Presents an easy to use wrapper for making games and samples.
35 public abstract class Game : IDisposable
37 GameClock clock = new GameClock();
38 GameTime gameTime = new GameTime();
39 TimeSpan maximumElapsedTime = TimeSpan.FromMilliseconds(500.0);
40 TimeSpan totalGameTime;
41 TimeSpan accumulatedElapsedGameTime;
42 TimeSpan lastFrameElapsedGameTime;
43 TimeSpan lastFrameElapsedRealTime;
44 TimeSpan targetElapsedTime = TimeSpan.FromTicks(166667);
45 TimeSpan inactiveSleepTime = TimeSpan.FromMilliseconds(20.0);
46 int updatesSinceRunningSlowly1 = int.MaxValue;
47 int updatesSinceRunningSlowly2 = int.MaxValue;
48 bool forceElapsedTimeToZero;
49 bool drawRunningSlowly;
54 /// Occurs when the game is disposed.
56 public event EventHandler Disposed;
59 /// Occurs when the game is activated.
61 public event EventHandler Activated;
64 /// Occurs when the game is deactivated.
66 public event EventHandler Deactivated;
69 /// Occurs when the game is exiting.
71 public event EventHandler Exiting;
74 /// Occurs when a drawing frame is about to start.
76 public event CancelEventHandler FrameStart;
79 /// Occurs when a drawing frame ends.
81 public event EventHandler FrameEnd;
84 /// Gets or sets the inactive sleep time.
86 /// <value>The inactive sleep time.</value>
87 public TimeSpan InactiveSleepTime
89 get { return inactiveSleepTime; }
93 if (value < TimeSpan.Zero)
94 throw new ArgumentOutOfRangeException("value", "Inactive sleep time cannot be less than zero.");
95 inactiveSleepTime = value;
100 /// Gets or sets the target elapsed time.
102 /// <value>The target elapsed time.</value>
103 public TimeSpan TargetElapsedTime
105 get { return targetElapsedTime; }
109 if (value <= TimeSpan.Zero)
110 throw new ArgumentOutOfRangeException("value", "Target elapsed time must be greater than zero.");
111 targetElapsedTime = value;
116 /// Gets or sets a value indicating whether the game is using a fixed time step.
119 /// <c>true</c> if the game is using a fixed time step; otherwise, <c>false</c>.
121 public bool IsFixedTimeStep
128 /// Gets a value indicating whether this <see cref="Game"/> is exiting.
130 /// <value><c>true</c> if exiting; otherwise, <c>false</c>.</value>
131 public bool IsExiting
138 /// Gets or sets a value indicating whether this instance is running.
141 /// <c>true</c> if this instance is running; otherwise, <c>false</c>.
143 public bool IsRunning
150 /// Gets the game window.
152 /// <value>The game window.</value>
153 public GameWindow Window
160 /// Gets the graphics device manager.
162 /// <value>The graphics device manager.</value>
163 public GraphicsDeviceManager GraphicsDeviceManager
170 /// Gets or sets a value indicating whether this <see cref="Game"/> is active.
172 /// <value><c>true</c> if active; otherwise, <c>false</c>.</value>
180 /// Initializes the <see cref="Game"/> class.
185 //Configuration.ThrowOnError = true;
186 //Configuration.AddResultWatch(ResultCode.DeviceLost, ResultWatchFlags.AlwaysIgnore);
187 //Configuration.AddResultWatch(ResultCode.WasStillDrawing, ResultWatchFlags.AlwaysIgnore);
190 //Configuration.DetectDoubleDispose = true;
191 Configuration.EnableObjectTracking = true;
192 //Configuration.EnableTrackingReleaseOnFinalizer = true;
193 //Configuration.EnableReleaseOnFinalizer = true;
195 //Configuration.DetectDoubleDispose = false;
196 Configuration.EnableObjectTracking = false;
199 // setup the application
200 Application.EnableVisualStyles();
201 Application.SetCompatibleTextRenderingDefault(false);
205 /// Initializes a new instance of the <see cref="Game"/> class.
209 IsFixedTimeStep = true;
211 Window = new GameWindow();
212 Window.ApplicationActivated += Window_ApplicationActivated;
213 Window.ApplicationDeactivated += Window_ApplicationDeactivated;
214 Window.Suspend += Window_Suspend;
215 Window.Resume += Window_Resume;
216 Window.Paint += Window_Paint;
218 GraphicsDeviceManager = new GraphicsDeviceManager(this);
222 /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
224 public void Dispose()
226 // GraphicsDeviceManager.Dispose will come around and call the Dispose(bool)
227 // overload, so we don't need to do it here. It's convoluted, but it works well.
228 if (GraphicsDeviceManager != null)
229 GraphicsDeviceManager.Dispose();
230 GraphicsDeviceManager = null;
232 if (Disposed != null)
233 Disposed(this, EventArgs.Empty);
235 GC.SuppressFinalize(this);
243 // request the game to terminate
256 gameTime.ElapsedGameTime = 0;
257 gameTime.ElapsedRealTime = 0;
258 gameTime.TotalGameTime = (float)totalGameTime.TotalSeconds;
259 gameTime.TotalRealTime = (float)clock.CurrentTime.TotalSeconds;
260 gameTime.IsRunningSlowly = false;
264 Application.Idle += Application_Idle;
265 Application.Run(Window);
269 Application.Idle -= Application_Idle;
271 OnExiting(EventArgs.Empty);
276 /// Performs one complete frame for the game.
280 // if we are exiting, do nothing
284 // if we are inactive, sleep for a bit
286 // Thread.Sleep((int)InactiveSleepTime.TotalMilliseconds);
290 gameTime.TotalRealTime = (float)clock.CurrentTime.TotalSeconds;
291 gameTime.ElapsedRealTime = (float)clock.ElapsedTime.TotalSeconds;
292 lastFrameElapsedRealTime += clock.ElapsedTime;
293 TimeSpan elapsedAdjustedTime = clock.ElapsedAdjustedTime;
294 if (elapsedAdjustedTime < TimeSpan.Zero)
295 elapsedAdjustedTime = TimeSpan.Zero;
297 if (forceElapsedTimeToZero)
299 gameTime.ElapsedRealTime = 0;
300 lastFrameElapsedRealTime = elapsedAdjustedTime = TimeSpan.Zero;
301 forceElapsedTimeToZero = false;
304 // cap the adjusted time
305 if (elapsedAdjustedTime > maximumElapsedTime)
306 elapsedAdjustedTime = maximumElapsedTime;
308 // check if we are using a fixed or variable time step
311 accumulatedElapsedGameTime += elapsedAdjustedTime;
312 long ratio = accumulatedElapsedGameTime.Ticks / TargetElapsedTime.Ticks;
313 accumulatedElapsedGameTime = TimeSpan.FromTicks(accumulatedElapsedGameTime.Ticks % TargetElapsedTime.Ticks);
314 lastFrameElapsedGameTime = TimeSpan.Zero;
317 TimeSpan targetElapsedTime = TargetElapsedTime;
321 updatesSinceRunningSlowly2 = updatesSinceRunningSlowly1;
322 updatesSinceRunningSlowly1 = 0;
326 if (updatesSinceRunningSlowly1 < int.MaxValue)
327 updatesSinceRunningSlowly1++;
328 if (updatesSinceRunningSlowly2 < int.MaxValue)
329 updatesSinceRunningSlowly2++;
332 drawRunningSlowly = updatesSinceRunningSlowly2 < 20;
334 // update until it's time to draw the next frame
335 while (ratio > 0 && !IsExiting)
341 gameTime.ElapsedGameTime = (float)targetElapsedTime.TotalSeconds;
342 gameTime.TotalGameTime = (float)totalGameTime.TotalSeconds;
343 gameTime.IsRunningSlowly = drawRunningSlowly;
349 lastFrameElapsedGameTime += targetElapsedTime;
350 totalGameTime += targetElapsedTime;
356 drawRunningSlowly = false;
357 updatesSinceRunningSlowly1 = int.MaxValue;
358 updatesSinceRunningSlowly2 = int.MaxValue;
360 // make sure we shouldn't be exiting
365 gameTime.ElapsedGameTime = 0;
366 lastFrameElapsedGameTime = elapsedAdjustedTime;
367 gameTime.TotalGameTime = (float)totalGameTime.TotalSeconds;
368 gameTime.IsRunningSlowly = false;
374 totalGameTime += elapsedAdjustedTime;
381 // refresh the FPS counter once per second
383 if ((float)clock.CurrentTime.TotalSeconds - lastUpdateTime > 1.0f)
385 gameTime.FramesPerSecond = (float)lastUpdateFrame / (float)(clock.CurrentTime.TotalSeconds - lastUpdateTime);
386 lastUpdateTime = (float)clock.CurrentTime.TotalSeconds;
392 /// Resets the elapsed time.
394 public void ResetElapsedTime()
396 forceElapsedTimeToZero = true;
397 updatesSinceRunningSlowly1 = int.MaxValue;
398 updatesSinceRunningSlowly2 = int.MaxValue;
402 /// Allows the game to perform logic processing.
404 /// <param name="gameTime">The time passed since the last update.</param>
405 protected virtual void Update(GameTime gameTime)
410 /// Called when a frame is ready to be drawn.
412 /// <param name="gameTime">The time passed since the last frame.</param>
413 protected virtual void Draw(GameTime gameTime)
418 /// Initializes the game.
420 protected internal virtual void Initialize()
425 /// Loads graphical resources.
427 protected internal virtual void LoadContent()
432 /// Unloads graphical resources.
434 protected internal virtual void UnloadContent()
439 /// Releases unmanaged and - optionally - managed resources
441 /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
442 protected internal virtual void Dispose(bool disposing)
447 /// Raises the <see cref="E:Activated"/> event.
449 /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
450 protected virtual void OnActivated(EventArgs e)
452 if (Activated != null)
457 /// Raises the <see cref="E:Deactivated"/> event.
459 /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
460 protected virtual void OnDeactivated(EventArgs e)
462 if (Deactivated != null)
463 Deactivated(this, e);
467 /// Raises the <see cref="E:Exiting"/> event.
469 /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
470 protected virtual void OnExiting(EventArgs e)
477 /// Raises the <see cref="E:FrameStart"/> event.
479 /// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
480 protected virtual void OnFrameStart(CancelEventArgs e)
482 if (FrameStart != null)
487 /// Raises the <see cref="E:FrameEnd"/> event.
489 /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
490 protected virtual void OnFrameEnd(EventArgs e)
492 if (FrameEnd != null)
500 if ( !IsExiting /* && !Window.IsMinimized */ ) // #28230 2012.5.1 yyagi
502 CancelEventArgs e = new CancelEventArgs(false);
506 gameTime.TotalRealTime = (float)clock.CurrentTime.TotalSeconds;
507 gameTime.ElapsedRealTime = (float)lastFrameElapsedRealTime.TotalSeconds;
508 gameTime.TotalGameTime = (float)totalGameTime.TotalSeconds;
509 gameTime.ElapsedGameTime = (float)lastFrameElapsedGameTime.TotalSeconds;
510 gameTime.IsRunningSlowly = drawRunningSlowly;
514 OnFrameEnd(EventArgs.Empty);
520 lastFrameElapsedGameTime = TimeSpan.Zero;
521 lastFrameElapsedRealTime = TimeSpan.Zero;
525 void Application_Idle(object sender, EventArgs e)
527 NativeMessage message;
528 while (!NativeMethods.PeekMessage(out message, IntPtr.Zero, 0, 0, 0))
537 void Window_ApplicationDeactivated(object sender, EventArgs e)
542 OnDeactivated(EventArgs.Empty);
546 void Window_ApplicationActivated(object sender, EventArgs e)
551 OnActivated(EventArgs.Empty);
555 void Window_Paint(object sender, PaintEventArgs e)
560 void Window_Resume(object sender, EventArgs e)
565 void Window_Suspend(object sender, EventArgs e)