OSDN Git Service

2dee62f8681f6e705a085d220e1228c9a4523258
[dtxmania/dtxmania.git] / FDK / コード / 01.フレームワーク / Core / Game.cs
1 /*
2 * Copyright (c) 2007-2009 SlimDX Group
3
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:
10
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13
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
20 * THE SOFTWARE.
21 */
22 using System;
23 using System.ComponentModel;
24 using System.Threading;
25 using System.Windows.Forms;
26 using SharpDX;
27 using SharpDX.Direct3D9;
28 using System.Collections.ObjectModel;
29
30 namespace SampleFramework
31 {
32     /// <summary>
33     /// Presents an easy to use wrapper for making games and samples.
34     /// </summary>
35     public abstract class Game : IDisposable
36     {
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;
50         long lastUpdateFrame;
51         float lastUpdateTime;
52
53         /// <summary>
54         /// Occurs when the game is disposed.
55         /// </summary>
56         public event EventHandler Disposed;
57
58         /// <summary>
59         /// Occurs when the game is activated.
60         /// </summary>
61         public event EventHandler Activated;
62
63         /// <summary>
64         /// Occurs when the game is deactivated.
65         /// </summary>
66         public event EventHandler Deactivated;
67
68         /// <summary>
69         /// Occurs when the game is exiting.
70         /// </summary>
71         public event EventHandler Exiting;
72
73         /// <summary>
74         /// Occurs when a drawing frame is about to start.
75         /// </summary>
76         public event CancelEventHandler FrameStart;
77
78         /// <summary>
79         /// Occurs when a drawing frame ends.
80         /// </summary>
81         public event EventHandler FrameEnd;
82
83         /// <summary>
84         /// Gets or sets the inactive sleep time.
85         /// </summary>
86         /// <value>The inactive sleep time.</value>
87         public TimeSpan InactiveSleepTime
88         {
89             get { return inactiveSleepTime; }
90             set
91             {
92                 // error checking
93                 if (value < TimeSpan.Zero)
94                     throw new ArgumentOutOfRangeException("value", "Inactive sleep time cannot be less than zero.");
95                 inactiveSleepTime = value;
96             }
97         }
98
99         /// <summary>
100         /// Gets or sets the target elapsed time.
101         /// </summary>
102         /// <value>The target elapsed time.</value>
103         public TimeSpan TargetElapsedTime
104         {
105             get { return targetElapsedTime; }
106             set
107             {
108                 // error checking
109                 if (value <= TimeSpan.Zero)
110                     throw new ArgumentOutOfRangeException("value", "Target elapsed time must be greater than zero.");
111                 targetElapsedTime = value;
112             }
113         }
114
115         /// <summary>
116         /// Gets or sets a value indicating whether the game is using a fixed time step.
117         /// </summary>
118         /// <value>
119         /// <c>true</c> if the game is using a fixed time step; otherwise, <c>false</c>.
120         /// </value>
121         public bool IsFixedTimeStep
122         {
123             get;
124             set;
125         }
126
127         /// <summary>
128         /// Gets a value indicating whether this <see cref="Game"/> is exiting.
129         /// </summary>
130         /// <value><c>true</c> if exiting; otherwise, <c>false</c>.</value>
131         public bool IsExiting
132         {
133             get;
134             private set;
135         }
136
137         /// <summary>
138         /// Gets or sets a value indicating whether this instance is running.
139         /// </summary>
140         /// <value>
141         /// <c>true</c> if this instance is running; otherwise, <c>false</c>.
142         /// </value>
143         public bool IsRunning
144         {
145             get;
146             private set;
147         }
148
149         /// <summary>
150         /// Gets the game window.
151         /// </summary>
152         /// <value>The game window.</value>
153         public GameWindow Window
154         {
155             get;
156             private set;
157         }
158
159         /// <summary>
160         /// Gets the graphics device manager.
161         /// </summary>
162         /// <value>The graphics device manager.</value>
163         public GraphicsDeviceManager GraphicsDeviceManager
164         {
165             get;
166             private set;
167         }
168
169         /// <summary>
170         /// Gets or sets a value indicating whether this <see cref="Game"/> is active.
171         /// </summary>
172         /// <value><c>true</c> if active; otherwise, <c>false</c>.</value>
173         public bool IsActive
174         {
175             get;
176             private set;
177         }
178
179         /// <summary>
180         /// Initializes the <see cref="Game"/> class.
181         /// </summary>
182         static Game()
183         {
184             // configure SlimDX
185             //Configuration.ThrowOnError = true;
186             //Configuration.AddResultWatch(ResultCode.DeviceLost, ResultWatchFlags.AlwaysIgnore);
187             //Configuration.AddResultWatch(ResultCode.WasStillDrawing, ResultWatchFlags.AlwaysIgnore);
188
189 #if DEBUG
190             //Configuration.DetectDoubleDispose = true;
191             Configuration.EnableObjectTracking = true;
192 #else
193             //Configuration.DetectDoubleDispose = false;
194             Configuration.EnableObjectTracking = false;
195 #endif
196
197             // setup the application
198             Application.EnableVisualStyles();
199             Application.SetCompatibleTextRenderingDefault(false);
200         }
201
202         /// <summary>
203         /// Initializes a new instance of the <see cref="Game"/> class.
204         /// </summary>
205         protected Game()
206         {
207             IsFixedTimeStep = true;
208
209             Window = new GameWindow();
210             Window.ApplicationActivated += Window_ApplicationActivated;
211             Window.ApplicationDeactivated += Window_ApplicationDeactivated;
212             Window.Suspend += Window_Suspend;
213             Window.Resume += Window_Resume;
214             Window.Paint += Window_Paint;
215
216             GraphicsDeviceManager = new GraphicsDeviceManager(this);
217         }
218
219         /// <summary>
220         /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
221         /// </summary>
222         public void Dispose()
223         {
224             // GraphicsDeviceManager.Dispose will come around and call the Dispose(bool)
225             // overload, so we don't need to do it here. It's convoluted, but it works well.
226             if (GraphicsDeviceManager != null)
227                 GraphicsDeviceManager.Dispose();
228             GraphicsDeviceManager = null;
229
230             if (Disposed != null)
231                 Disposed(this, EventArgs.Empty);
232
233             GC.SuppressFinalize(this);
234         }
235
236         /// <summary>
237         /// Exits the game.
238         /// </summary>
239         public void Exit()
240         {
241             // request the game to terminate
242             IsExiting = true;
243         }
244
245         /// <summary>
246         /// Runs the game.
247         /// </summary>
248         public void Run()
249         {
250             IsRunning = true;
251
252             try
253             {
254                 gameTime.ElapsedGameTime = 0;
255                 gameTime.ElapsedRealTime = 0;
256                 gameTime.TotalGameTime = (float)totalGameTime.TotalSeconds;
257                 gameTime.TotalRealTime = (float)clock.CurrentTime.TotalSeconds;
258                 gameTime.IsRunningSlowly = false;
259
260                 Update(gameTime);
261
262                 Application.Idle += Application_Idle;
263                 Application.Run(Window);
264             }
265             finally
266             {
267                 Application.Idle -= Application_Idle;
268                 IsRunning = false;
269                 OnExiting(EventArgs.Empty);
270             }
271         }
272
273         /// <summary>
274         /// Performs one complete frame for the game.
275         /// </summary>
276         public void Tick()
277         {
278             // if we are exiting, do nothing
279             if (IsExiting)
280                 return;
281
282             // if we are inactive, sleep for a bit
283             //if (!IsActive)
284             //    Thread.Sleep((int)InactiveSleepTime.TotalMilliseconds);
285
286             clock.Step();
287
288             gameTime.TotalRealTime = (float)clock.CurrentTime.TotalSeconds;
289             gameTime.ElapsedRealTime = (float)clock.ElapsedTime.TotalSeconds;
290             lastFrameElapsedRealTime += clock.ElapsedTime;
291             TimeSpan elapsedAdjustedTime = clock.ElapsedAdjustedTime;
292             if (elapsedAdjustedTime < TimeSpan.Zero)
293                 elapsedAdjustedTime = TimeSpan.Zero;
294
295             if (forceElapsedTimeToZero)
296             {
297                 gameTime.ElapsedRealTime = 0;
298                 lastFrameElapsedRealTime = elapsedAdjustedTime = TimeSpan.Zero;
299                 forceElapsedTimeToZero = false;
300             }
301
302             // cap the adjusted time
303             if (elapsedAdjustedTime > maximumElapsedTime)
304                 elapsedAdjustedTime = maximumElapsedTime;
305
306             // check if we are using a fixed or variable time step
307             if (IsFixedTimeStep)
308             {
309                 accumulatedElapsedGameTime += elapsedAdjustedTime;
310                 long ratio = accumulatedElapsedGameTime.Ticks / TargetElapsedTime.Ticks;
311                 accumulatedElapsedGameTime = TimeSpan.FromTicks(accumulatedElapsedGameTime.Ticks % TargetElapsedTime.Ticks);
312                 lastFrameElapsedGameTime = TimeSpan.Zero;
313                 if (ratio == 0)
314                     return;
315                 TimeSpan targetElapsedTime = TargetElapsedTime;
316
317                 if (ratio > 1)
318                 {
319                     updatesSinceRunningSlowly2 = updatesSinceRunningSlowly1;
320                     updatesSinceRunningSlowly1 = 0;
321                 }
322                 else
323                 {
324                     if (updatesSinceRunningSlowly1 < int.MaxValue)
325                         updatesSinceRunningSlowly1++;
326                     if (updatesSinceRunningSlowly2 < int.MaxValue)
327                         updatesSinceRunningSlowly2++;
328                 }
329
330                 drawRunningSlowly = updatesSinceRunningSlowly2 < 20;
331
332                 // update until it's time to draw the next frame
333                 while (ratio > 0 && !IsExiting)
334                 {
335                     ratio -= 1;
336
337                     try
338                     {
339                         gameTime.ElapsedGameTime = (float)targetElapsedTime.TotalSeconds;
340                         gameTime.TotalGameTime = (float)totalGameTime.TotalSeconds;
341                         gameTime.IsRunningSlowly = drawRunningSlowly;
342
343                         Update(gameTime);
344                     }
345                     finally
346                     {
347                         lastFrameElapsedGameTime += targetElapsedTime;
348                         totalGameTime += targetElapsedTime;
349                     }
350                 }
351             }
352             else
353             {
354                 drawRunningSlowly = false;
355                 updatesSinceRunningSlowly1 = int.MaxValue;
356                 updatesSinceRunningSlowly2 = int.MaxValue;
357
358                 // make sure we shouldn't be exiting
359                 if (!IsExiting)
360                 {
361                     try
362                     {
363                         gameTime.ElapsedGameTime = 0;
364                         lastFrameElapsedGameTime = elapsedAdjustedTime;
365                         gameTime.TotalGameTime = (float)totalGameTime.TotalSeconds;
366                         gameTime.IsRunningSlowly = false;
367
368                         Update(gameTime);
369                     }
370                     finally
371                     {
372                         totalGameTime += elapsedAdjustedTime;
373                     }
374                 }
375             }
376
377             DrawFrame();
378
379             // refresh the FPS counter once per second
380             lastUpdateFrame++;
381             if ((float)clock.CurrentTime.TotalSeconds - lastUpdateTime > 1.0f)
382             {
383                 gameTime.FramesPerSecond = (float)lastUpdateFrame / (float)(clock.CurrentTime.TotalSeconds - lastUpdateTime);
384                 lastUpdateTime = (float)clock.CurrentTime.TotalSeconds;
385                 lastUpdateFrame = 0;
386             }
387         }
388
389         /// <summary>
390         /// Resets the elapsed time.
391         /// </summary>
392         public void ResetElapsedTime()
393         {
394             forceElapsedTimeToZero = true;
395             updatesSinceRunningSlowly1 = int.MaxValue;
396             updatesSinceRunningSlowly2 = int.MaxValue;
397         }
398
399         /// <summary>
400         /// Allows the game to perform logic processing.
401         /// </summary>
402         /// <param name="gameTime">The time passed since the last update.</param>
403         protected virtual void Update(GameTime gameTime)
404         {
405         }
406
407         /// <summary>
408         /// Called when a frame is ready to be drawn.
409         /// </summary>
410         /// <param name="gameTime">The time passed since the last frame.</param>
411         protected virtual void Draw(GameTime gameTime)
412         {
413         }
414
415         /// <summary>
416         /// Initializes the game.
417         /// </summary>
418         protected internal virtual void Initialize()
419         {
420         }
421
422         /// <summary>
423         /// Loads graphical resources.
424         /// </summary>
425         protected internal virtual void LoadContent()
426         {
427         }
428
429         /// <summary>
430         /// Unloads graphical resources.
431         /// </summary>
432         protected internal virtual void UnloadContent()
433         {
434         }
435
436         /// <summary>
437         /// Releases unmanaged and - optionally - managed resources
438         /// </summary>
439         /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
440         protected internal virtual void Dispose(bool disposing)
441         {
442         }
443
444         /// <summary>
445         /// Raises the <see cref="E:Activated"/> event.
446         /// </summary>
447         /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
448         protected virtual void OnActivated(EventArgs e)
449         {
450             if (Activated != null)
451                 Activated(this, e);
452         }
453
454         /// <summary>
455         /// Raises the <see cref="E:Deactivated"/> event.
456         /// </summary>
457         /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
458         protected virtual void OnDeactivated(EventArgs e)
459         {
460             if (Deactivated != null)
461                 Deactivated(this, e);
462         }
463
464         /// <summary>
465         /// Raises the <see cref="E:Exiting"/> event.
466         /// </summary>
467         /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
468         protected virtual void OnExiting(EventArgs e)
469         {
470             if (Exiting != null)
471                 Exiting(this, e);
472         }
473
474         /// <summary>
475         /// Raises the <see cref="E:FrameStart"/> event.
476         /// </summary>
477         /// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
478         protected virtual void OnFrameStart(CancelEventArgs e)
479         {
480             if (FrameStart != null)
481                 FrameStart(this, e);
482         }
483
484         /// <summary>
485         /// Raises the <see cref="E:FrameEnd"/> event.
486         /// </summary>
487         /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
488         protected virtual void OnFrameEnd(EventArgs e)
489         {
490             if (FrameEnd != null)
491                 FrameEnd(this, e);
492         }
493
494         void DrawFrame()
495         {
496             try
497             {
498                                 if ( !IsExiting /* && !Window.IsMinimized */ )          // #28230 2012.5.1 yyagi
499                                 {
500                     CancelEventArgs e = new CancelEventArgs(false);
501                     OnFrameStart(e);
502                     if (!e.Cancel)
503                     {
504                         gameTime.TotalRealTime = (float)clock.CurrentTime.TotalSeconds;
505                         gameTime.ElapsedRealTime = (float)lastFrameElapsedRealTime.TotalSeconds;
506                         gameTime.TotalGameTime = (float)totalGameTime.TotalSeconds;
507                         gameTime.ElapsedGameTime = (float)lastFrameElapsedGameTime.TotalSeconds;
508                         gameTime.IsRunningSlowly = drawRunningSlowly;
509
510                         Draw(gameTime);
511
512                         OnFrameEnd(EventArgs.Empty);
513                     }
514                 }
515             }
516             finally
517             {
518                 lastFrameElapsedGameTime = TimeSpan.Zero;
519                 lastFrameElapsedRealTime = TimeSpan.Zero;
520             }
521         }
522
523         void Application_Idle(object sender, EventArgs e)
524         {
525             NativeMessage message;
526             while (!NativeMethods.PeekMessage(out message, IntPtr.Zero, 0, 0, 0))
527             {
528                 if (IsExiting)
529                     Window.Close();
530                 else
531                     Tick();
532             }
533         }
534
535         void Window_ApplicationDeactivated(object sender, EventArgs e)
536         {
537             if (IsActive)
538             {
539                 IsActive = false;
540                 OnDeactivated(EventArgs.Empty);
541             }
542         }
543
544         void Window_ApplicationActivated(object sender, EventArgs e)
545         {
546             if (!IsActive)
547             {
548                 IsActive = true;
549                 OnActivated(EventArgs.Empty);
550             }
551         }
552
553         void Window_Paint(object sender, PaintEventArgs e)
554         {
555             DrawFrame();
556         }
557
558         void Window_Resume(object sender, EventArgs e)
559         {
560             clock.Resume();
561         }
562
563         void Window_Suspend(object sender, EventArgs e)
564         {
565             clock.Suspend();
566         }
567     }
568 }