1
0
mirror of synced 2024-12-21 11:55:59 +01:00
OpenTaiko/FDK19/コード/01.フレームワーク/Core/Game.cs
2021-09-21 00:16:38 +02:00

570 lines
18 KiB
C#

/*
* Copyright (c) 2007-2009 SlimDX Group
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
using SlimDX;
using SlimDX.Direct3D9;
using System.Collections.ObjectModel;
namespace SampleFramework
{
/// <summary>
/// Presents an easy to use wrapper for making games and samples.
/// </summary>
public abstract class Game : IDisposable
{
GameClock clock = new GameClock();
GameTime gameTime = new GameTime();
TimeSpan maximumElapsedTime = TimeSpan.FromMilliseconds(500.0);
TimeSpan totalGameTime;
TimeSpan accumulatedElapsedGameTime;
TimeSpan lastFrameElapsedGameTime;
TimeSpan lastFrameElapsedRealTime;
TimeSpan targetElapsedTime = TimeSpan.FromTicks(166667);
TimeSpan inactiveSleepTime = TimeSpan.FromMilliseconds(20.0);
int updatesSinceRunningSlowly1 = int.MaxValue;
int updatesSinceRunningSlowly2 = int.MaxValue;
bool forceElapsedTimeToZero;
bool drawRunningSlowly;
long lastUpdateFrame;
float lastUpdateTime;
/// <summary>
/// Occurs when the game is disposed.
/// </summary>
public event EventHandler Disposed;
/// <summary>
/// Occurs when the game is activated.
/// </summary>
public event EventHandler Activated;
/// <summary>
/// Occurs when the game is deactivated.
/// </summary>
public event EventHandler Deactivated;
/// <summary>
/// Occurs when the game is exiting.
/// </summary>
public event EventHandler Exiting;
/// <summary>
/// Occurs when a drawing frame is about to start.
/// </summary>
public event CancelEventHandler FrameStart;
/// <summary>
/// Occurs when a drawing frame ends.
/// </summary>
public event EventHandler FrameEnd;
/// <summary>
/// Gets or sets the inactive sleep time.
/// </summary>
/// <value>The inactive sleep time.</value>
public TimeSpan InactiveSleepTime
{
get { return inactiveSleepTime; }
set
{
// error checking
if (value < TimeSpan.Zero)
throw new ArgumentOutOfRangeException("value", "Inactive sleep time cannot be less than zero.");
inactiveSleepTime = value;
}
}
/// <summary>
/// Gets or sets the target elapsed time.
/// </summary>
/// <value>The target elapsed time.</value>
public TimeSpan TargetElapsedTime
{
get { return targetElapsedTime; }
set
{
// error checking
if (value <= TimeSpan.Zero)
throw new ArgumentOutOfRangeException("value", "Target elapsed time must be greater than zero.");
targetElapsedTime = value;
}
}
/// <summary>
/// Gets or sets a value indicating whether the game is using a fixed time step.
/// </summary>
/// <value>
/// <c>true</c> if the game is using a fixed time step; otherwise, <c>false</c>.
/// </value>
public bool IsFixedTimeStep
{
get;
set;
}
/// <summary>
/// Gets a value indicating whether this <see cref="Game"/> is exiting.
/// </summary>
/// <value><c>true</c> if exiting; otherwise, <c>false</c>.</value>
public bool IsExiting
{
get;
private set;
}
/// <summary>
/// Gets or sets a value indicating whether this instance is running.
/// </summary>
/// <value>
/// <c>true</c> if this instance is running; otherwise, <c>false</c>.
/// </value>
public bool IsRunning
{
get;
private set;
}
/// <summary>
/// Gets the game window.
/// </summary>
/// <value>The game window.</value>
public GameWindow Window
{
get;
private set;
}
/// <summary>
/// Gets the graphics device manager.
/// </summary>
/// <value>The graphics device manager.</value>
public GraphicsDeviceManager GraphicsDeviceManager
{
get;
private set;
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="Game"/> is active.
/// </summary>
/// <value><c>true</c> if active; otherwise, <c>false</c>.</value>
public bool IsActive
{
get;
private set;
}
/// <summary>
/// Initializes the <see cref="Game"/> class.
/// </summary>
static Game()
{
// configure SlimDX
Configuration.ThrowOnError = true;
Configuration.AddResultWatch(ResultCode.DeviceLost, ResultWatchFlags.AlwaysIgnore);
Configuration.AddResultWatch(ResultCode.WasStillDrawing, ResultWatchFlags.AlwaysIgnore);
#if DEBUG
Configuration.DetectDoubleDispose = true;
Configuration.EnableObjectTracking = true;
#else
Configuration.DetectDoubleDispose = false;
Configuration.EnableObjectTracking = false;
#endif
// setup the application
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
}
/// <summary>
/// Initializes a new instance of the <see cref="Game"/> class.
/// </summary>
protected Game()
{
IsFixedTimeStep = true;
Window = new GameWindow();
Window.ApplicationActivated += Window_ApplicationActivated;
Window.ApplicationDeactivated += Window_ApplicationDeactivated;
Window.Suspend += Window_Suspend;
Window.Resume += Window_Resume;
Window.Paint += Window_Paint;
GraphicsDeviceManager = new GraphicsDeviceManager(this);
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
// GraphicsDeviceManager.Dispose will come around and call the Dispose(bool)
// overload, so we don't need to do it here. It's convoluted, but it works well.
if (GraphicsDeviceManager != null)
GraphicsDeviceManager.Dispose();
GraphicsDeviceManager = null;
if (Disposed != null)
Disposed(this, EventArgs.Empty);
GC.SuppressFinalize(this);
}
/// <summary>
/// Exits the game.
/// </summary>
public void Exit()
{
// request the game to terminate
IsExiting = true;
}
/// <summary>
/// Runs the game.
/// </summary>
public void Run()
{
IsRunning = true;
try
{
gameTime.ElapsedGameTime = 0;
gameTime.ElapsedRealTime = 0;
gameTime.TotalGameTime = (float)totalGameTime.TotalSeconds;
gameTime.TotalRealTime = (float)clock.CurrentTime.TotalSeconds;
gameTime.IsRunningSlowly = false;
Update(gameTime);
Application.Idle += Application_Idle;
Application.Run(Window);
}
finally
{
Application.Idle -= Application_Idle;
IsRunning = false;
OnExiting(EventArgs.Empty);
}
}
/// <summary>
/// Performs one complete frame for the game.
/// </summary>
public void Tick()
{
// if we are exiting, do nothing
if (IsExiting)
return;
// if we are inactive, sleep for a bit
//if (!IsActive)
// Thread.Sleep((int)InactiveSleepTime.TotalMilliseconds);
clock.Step();
gameTime.TotalRealTime = (float)clock.CurrentTime.TotalSeconds;
gameTime.ElapsedRealTime = (float)clock.ElapsedTime.TotalSeconds;
lastFrameElapsedRealTime += clock.ElapsedTime;
TimeSpan elapsedAdjustedTime = clock.ElapsedAdjustedTime;
if (elapsedAdjustedTime < TimeSpan.Zero)
elapsedAdjustedTime = TimeSpan.Zero;
if (forceElapsedTimeToZero)
{
gameTime.ElapsedRealTime = 0;
lastFrameElapsedRealTime = elapsedAdjustedTime = TimeSpan.Zero;
forceElapsedTimeToZero = false;
}
// cap the adjusted time
if (elapsedAdjustedTime > maximumElapsedTime)
elapsedAdjustedTime = maximumElapsedTime;
// check if we are using a fixed or variable time step
if (IsFixedTimeStep)
{
accumulatedElapsedGameTime += elapsedAdjustedTime;
long ratio = accumulatedElapsedGameTime.Ticks / TargetElapsedTime.Ticks;
accumulatedElapsedGameTime = TimeSpan.FromTicks(accumulatedElapsedGameTime.Ticks % TargetElapsedTime.Ticks);
lastFrameElapsedGameTime = TimeSpan.Zero;
if (ratio == 0)
return;
TimeSpan targetElapsedTime = TargetElapsedTime;
if (ratio > 1)
{
updatesSinceRunningSlowly2 = updatesSinceRunningSlowly1;
updatesSinceRunningSlowly1 = 0;
}
else
{
if (updatesSinceRunningSlowly1 < int.MaxValue)
updatesSinceRunningSlowly1++;
if (updatesSinceRunningSlowly2 < int.MaxValue)
updatesSinceRunningSlowly2++;
}
drawRunningSlowly = updatesSinceRunningSlowly2 < 20;
// update until it's time to draw the next frame
while (ratio > 0 && !IsExiting)
{
ratio -= 1;
try
{
gameTime.ElapsedGameTime = (float)targetElapsedTime.TotalSeconds;
gameTime.TotalGameTime = (float)totalGameTime.TotalSeconds;
gameTime.IsRunningSlowly = drawRunningSlowly;
Update(gameTime);
}
finally
{
lastFrameElapsedGameTime += targetElapsedTime;
totalGameTime += targetElapsedTime;
}
}
}
else
{
drawRunningSlowly = false;
updatesSinceRunningSlowly1 = int.MaxValue;
updatesSinceRunningSlowly2 = int.MaxValue;
// make sure we shouldn't be exiting
if (!IsExiting)
{
try
{
gameTime.ElapsedGameTime = 0;
lastFrameElapsedGameTime = elapsedAdjustedTime;
gameTime.TotalGameTime = (float)totalGameTime.TotalSeconds;
gameTime.IsRunningSlowly = false;
Update(gameTime);
}
finally
{
totalGameTime += elapsedAdjustedTime;
}
}
}
DrawFrame();
// refresh the FPS counter once per second
lastUpdateFrame++;
if ((float)clock.CurrentTime.TotalSeconds - lastUpdateTime > 1.0f)
{
gameTime.FramesPerSecond = (float)lastUpdateFrame / (float)(clock.CurrentTime.TotalSeconds - lastUpdateTime);
lastUpdateTime = (float)clock.CurrentTime.TotalSeconds;
lastUpdateFrame = 0;
}
}
/// <summary>
/// Resets the elapsed time.
/// </summary>
public void ResetElapsedTime()
{
forceElapsedTimeToZero = true;
updatesSinceRunningSlowly1 = int.MaxValue;
updatesSinceRunningSlowly2 = int.MaxValue;
}
/// <summary>
/// Allows the game to perform logic processing.
/// </summary>
/// <param name="gameTime">The time passed since the last update.</param>
protected virtual void Update(GameTime gameTime)
{
}
/// <summary>
/// Called when a frame is ready to be drawn.
/// </summary>
/// <param name="gameTime">The time passed since the last frame.</param>
protected virtual void Draw(GameTime gameTime)
{
}
/// <summary>
/// Initializes the game.
/// </summary>
protected internal virtual void Initialize()
{
}
/// <summary>
/// Loads graphical resources.
/// </summary>
protected internal virtual void LoadContent()
{
}
/// <summary>
/// Unloads graphical resources.
/// </summary>
protected internal virtual void UnloadContent()
{
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected internal virtual void Dispose(bool disposing)
{
}
/// <summary>
/// Raises the <see cref="E:Activated"/> event.
/// </summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected virtual void OnActivated(EventArgs e)
{
if (Activated != null)
Activated(this, e);
}
/// <summary>
/// Raises the <see cref="E:Deactivated"/> event.
/// </summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected virtual void OnDeactivated(EventArgs e)
{
if (Deactivated != null)
Deactivated(this, e);
}
/// <summary>
/// Raises the <see cref="E:Exiting"/> event.
/// </summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected virtual void OnExiting(EventArgs e)
{
if (Exiting != null)
Exiting(this, e);
}
/// <summary>
/// Raises the <see cref="E:FrameStart"/> event.
/// </summary>
/// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
protected virtual void OnFrameStart(CancelEventArgs e)
{
if (FrameStart != null)
FrameStart(this, e);
}
/// <summary>
/// Raises the <see cref="E:FrameEnd"/> event.
/// </summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected virtual void OnFrameEnd(EventArgs e)
{
if (FrameEnd != null)
FrameEnd(this, e);
}
void DrawFrame()
{
try
{
if ( !IsExiting /* && !Window.IsMinimized */ ) // #28230 2012.5.1 yyagi
{
CancelEventArgs e = new CancelEventArgs(false);
OnFrameStart(e);
if (!e.Cancel)
{
gameTime.TotalRealTime = (float)clock.CurrentTime.TotalSeconds;
gameTime.ElapsedRealTime = (float)lastFrameElapsedRealTime.TotalSeconds;
gameTime.TotalGameTime = (float)totalGameTime.TotalSeconds;
gameTime.ElapsedGameTime = (float)lastFrameElapsedGameTime.TotalSeconds;
gameTime.IsRunningSlowly = drawRunningSlowly;
Draw(gameTime);
OnFrameEnd(EventArgs.Empty);
}
}
}
finally
{
lastFrameElapsedGameTime = TimeSpan.Zero;
lastFrameElapsedRealTime = TimeSpan.Zero;
}
}
void Application_Idle(object sender, EventArgs e)
{
NativeMessage message;
while (!NativeMethods.PeekMessage(out message, IntPtr.Zero, 0, 0, 0))
{
if (IsExiting)
Window.Close();
else
Tick();
}
}
void Window_ApplicationDeactivated(object sender, EventArgs e)
{
if (IsActive)
{
IsActive = false;
OnDeactivated(EventArgs.Empty);
}
}
void Window_ApplicationActivated(object sender, EventArgs e)
{
if (!IsActive)
{
IsActive = true;
OnActivated(EventArgs.Empty);
}
}
void Window_Paint(object sender, PaintEventArgs e)
{
DrawFrame();
}
void Window_Resume(object sender, EventArgs e)
{
clock.Resume();
}
void Window_Suspend(object sender, EventArgs e)
{
clock.Suspend();
}
}
}