using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
// ************************************************************************* //
// Disable 'missing xml comment' warning for this file since it has been
// generated by .NET Reflector and I'm not going to comment this all
// just so I have to start from scratch again when XNA gets updated
#pragma warning disable 1591
// ************************************************************************* //
namespace Nuclex
{
/// Provides basic graphics device initialization, game logic, and rendering code.
public partial class GameControl : UserControl
{
/// Initializes a new instance of this class, which provides basic graphics device initialization, game logic, rendering code, and a game loop.
public GameControl()
{
this.maximumElapsedTime = TimeSpan.FromMilliseconds(500.0);
this.gameTime = new GameTime();
this.isFixedTimeStep = true;
this.updatesSinceRunningSlowly1 = 0x7fffffff;
this.updatesSinceRunningSlowly2 = 0x7fffffff;
this.updateableComponents = new List();
this.currentlyUpdatingComponents = new List();
this.drawableComponents = new List();
this.currentlyDrawingComponents = new List();
this.notYetInitialized = new List();
this.gameServices = new GameServiceContainer();
this.EnsureHost();
this.gameComponents = new GameComponentCollection();
this.gameComponents.ComponentAdded += new EventHandler(this.GameComponentAdded);
this.gameComponents.ComponentRemoved += new EventHandler(this.GameComponentRemoved);
this.Paint += new PaintEventHandler(DoPaint);
this.clock = new GameClock();
this.totalGameTime = TimeSpan.Zero;
this.accumulatedElapsedGameTime = TimeSpan.Zero;
this.lastFrameElapsedGameTime = TimeSpan.Zero;
this.targetElapsedTime = TimeSpan.FromTicks((long)0x28b0a);
this.inactiveSleepTime = TimeSpan.FromMilliseconds(20);
this.graphics = new GraphicsDeviceManager(this);
this.graphicsDeviceManager = this.graphics;
}
#region Specialized UserControl Customizations
protected override void OnLoad(EventArgs e)
{
// Upper part of Run() method
base.OnLoad(e);
this.graphicsDeviceManager = this.Services.GetService(typeof(IGraphicsDeviceManager)) as IGraphicsDeviceManager;
if (this.graphicsDeviceManager != null)
{
this.graphicsDeviceManager.CreateDevice();
}
try
{
this.Initialize();
}
catch {
}
this.inRun = true;
try
{
this.BeginRun();
this.gameTime = new GameTime(
this.clock.CurrentTime, TimeSpan.Zero,
this.totalGameTime, TimeSpan.Zero,
false
);
this.Update(this.gameTime);
this.doneFirstUpdate = true;
if (this.host != null)
{
this.host.PreRun();
}
}
catch (Exception)
{
this.inRun = false;
//throw;
}
}
protected override void WndProc(ref Message m)
{
//const int WM_CLOSE = 16;
const int WM_DESTROY = 2;
if (m.Msg == WM_DESTROY)
{
// Lower part of Run() method
try
{
if (this.host != null)
{
this.host.PostRun();
}
this.EndRun();
}
finally
{
this.inRun = false;
}
}
base.WndProc(ref m);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// We simply don't draw the background to prevent flickering
//base.OnPaintBackground(e);
}
#endregion
///
/// Starts the drawing of a frame. This method is followed by calls to Game.Draw and Game.EndDraw.
///
/// true if the frame should be drawn; false otherwise.
protected virtual bool BeginDraw()
{
if ((this.graphicsDeviceManager != null) && !this.graphicsDeviceManager.BeginDraw())
{
return false;
}
return true;
}
///
/// Called after all components are initialized but before the first update in the game loop.
///
protected virtual void BeginRun()
{
}
private void DeviceCreated(object sender, EventArgs e)
{
this.LoadGraphicsContent(true);
this.LoadContent();
}
private void DeviceDisposing(object sender, EventArgs e)
{
this.UnloadGraphicsContent(true);
this.UnloadContent();
}
private void DeviceReset(object sender, EventArgs e)
{
this.LoadGraphicsContent(false);
}
private void DeviceResetting(object sender, EventArgs e)
{
this.UnloadGraphicsContent(false);
}
///
/// Releases all resources used by the Game class.
///
private void InternalDispose()
{
lock (this)
{
IGameComponent[] array = new IGameComponent[this.gameComponents.Count];
this.gameComponents.CopyTo(array, 0);
for (int i = 0; i < array.Length; i++)
{
IDisposable disposable = array[i] as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
IDisposable disposable2 = this.graphicsDeviceManager as IDisposable;
if (disposable2 != null)
{
disposable2.Dispose();
}
this.UnhookDeviceEvents();
// TODO: Check that base.Dispose actually fires the Disposed event!
//if(this.Disposed != null) {
// this.Disposed(this, EventArgs.Empty);
//}
}
}
/// Called when the game determines it is time to draw a frame. Override this method with game-specific rendering code.
/// Time passed since the last call to Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime).
protected virtual void Draw(GameTime gameTime)
{
for (int i = 0; i < this.drawableComponents.Count; i++)
{
this.currentlyDrawingComponents.Add(this.drawableComponents[i]);
}
for (int j = 0; j < this.currentlyDrawingComponents.Count; j++)
{
IDrawable drawable = this.currentlyDrawingComponents[j];
if (drawable.Visible)
{
drawable.Draw(gameTime);
}
}
this.currentlyDrawingComponents.Clear();
}
private void DrawableDrawOrderChanged(object sender, EventArgs e)
{
IDrawable item = sender as IDrawable;
this.drawableComponents.Remove(item);
int num = this.drawableComponents.BinarySearch(item, DrawOrderComparer.Default);
if (num < 0)
{
num = ~num;
while ((num < this.drawableComponents.Count) && (this.drawableComponents[num].DrawOrder == item.DrawOrder))
{
num++;
}
this.drawableComponents.Insert(num, item);
}
}
private void DrawFrame()
{
try
{
if ((this.doneFirstUpdate /*&& !this.Window.IsMinimized*/) && this.BeginDraw())
{
this.gameTime = new GameTime(
this.clock.CurrentTime, this.lastFrameElapsedRealTime,
this.totalGameTime, this.lastFrameElapsedGameTime,
this.drawRunningSlowly
);
this.Draw(this.gameTime);
this.EndDraw();
}
}
finally
{
this.lastFrameElapsedRealTime = TimeSpan.Zero;
this.lastFrameElapsedGameTime = TimeSpan.Zero;
}
}
///
/// Ends the drawing of a frame. This method is preceeded by calls to Game.Draw and Game.BeginDraw.
///
protected virtual void EndDraw()
{
if (this.graphicsDeviceManager != null)
{
this.graphicsDeviceManager.EndDraw();
}
}
/// Called after the game loop has stopped running before exiting.
protected virtual void EndRun()
{
}
private void EnsureHost()
{
if (this.host == null)
{
this.host = new WindowsGameHost(this);
this.host.Activated += new EventHandler(this.HostActivated);
this.host.Deactivated += new EventHandler(this.HostDeactivated);
this.host.Suspend += new EventHandler(this.HostSuspend);
this.host.Resume += new EventHandler(this.HostResume);
this.host.Idle += new EventHandler(this.HostIdle);
this.host.Exiting += new EventHandler(this.HostExiting);
}
}
/// Exits the game.
public void Exit()
{
this.exitRequested = true;
this.host.Exit();
}
~GameControl()
{
this.Dispose(false);
}
private void GameComponentAdded(object sender, GameComponentCollectionEventArgs e)
{
if (this.inRun)
{
e.GameComponent.Initialize();
}
else
{
this.notYetInitialized.Add(e.GameComponent);
}
IUpdateable gameComponent = e.GameComponent as IUpdateable;
if (gameComponent != null)
{
int index = this.updateableComponents.BinarySearch(gameComponent, UpdateOrderComparer.Default);
if (index < 0)
{
index = ~index;
while ((index < this.updateableComponents.Count) && (this.updateableComponents[index].UpdateOrder == gameComponent.UpdateOrder))
{
index++;
}
this.updateableComponents.Insert(index, gameComponent);
gameComponent.UpdateOrderChanged += new EventHandler(this.UpdateableUpdateOrderChanged);
}
}
IDrawable item = e.GameComponent as IDrawable;
if (item != null)
{
int num2 = this.drawableComponents.BinarySearch(item, DrawOrderComparer.Default);
if (num2 < 0)
{
num2 = ~num2;
while ((num2 < this.drawableComponents.Count) && (this.drawableComponents[num2].DrawOrder == item.DrawOrder))
{
num2++;
}
this.drawableComponents.Insert(num2, item);
item.DrawOrderChanged += new EventHandler(this.DrawableDrawOrderChanged);
}
}
}
private void GameComponentRemoved(object sender, GameComponentCollectionEventArgs e)
{
if (!this.inRun)
{
this.notYetInitialized.Remove(e.GameComponent);
}
IUpdateable gameComponent = e.GameComponent as IUpdateable;
if (gameComponent != null)
{
this.updateableComponents.Remove(gameComponent);
gameComponent.UpdateOrderChanged -= new EventHandler(this.UpdateableUpdateOrderChanged);
}
IDrawable item = e.GameComponent as IDrawable;
if (item != null)
{
this.drawableComponents.Remove(item);
item.DrawOrderChanged -= new EventHandler(this.DrawableDrawOrderChanged);
}
}
private void HookDeviceEvents()
{
this.graphicsDeviceService = this.Services.GetService(typeof(IGraphicsDeviceService)) as IGraphicsDeviceService;
if (this.graphicsDeviceService != null)
{
this.graphicsDeviceService.DeviceCreated += new EventHandler(this.DeviceCreated);
this.graphicsDeviceService.DeviceResetting += new EventHandler(this.DeviceResetting);
this.graphicsDeviceService.DeviceReset += new EventHandler(this.DeviceReset);
this.graphicsDeviceService.DeviceDisposing += new EventHandler(this.DeviceDisposing);
}
}
private void HostActivated(object sender, EventArgs e)
{
if (!this.isActive)
{
this.isActive = true;
this.OnActivated(this, EventArgs.Empty);
}
}
private void HostDeactivated(object sender, EventArgs e)
{
if (this.isActive)
{
this.isActive = false;
this.OnDeactivated(this, EventArgs.Empty);
}
}
private void HostExiting(object sender, EventArgs e)
{
this.OnExiting(this, EventArgs.Empty);
}
private void HostIdle(object sender, EventArgs e)
{
this.Tick();
}
private void HostResume(object sender, EventArgs e)
{
this.clock.Resume();
}
private void HostSuspend(object sender, EventArgs e)
{
this.clock.Suspend();
}
///
/// Called after the Game and Graphics.GraphicsDevice are created, but before Game.LoadContent.
///
protected virtual void Initialize()
{
this.HookDeviceEvents();
while (this.notYetInitialized.Count != 0)
{
this.notYetInitialized[0].Initialize();
this.notYetInitialized.RemoveAt(0);
}
if ((this.graphicsDeviceService != null) && (this.graphicsDeviceService.GraphicsDevice != null))
{
this.LoadGraphicsContent(true);
this.LoadContent();
}
}
protected virtual void LoadGraphicsContent(bool loadAllContent)
{
}
/// Called when graphics resources need to be loaded. Override this method to load any game-specific graphics resources.
protected virtual void LoadContent()
{
}
/// Raises the Game.Activated event. Override this method to add code to handle when the game gains focus.
/// The Game.
/// Arguments for the Game.Activated event.
protected virtual void OnActivated(object sender, EventArgs args)
{
if (this.Activated != null)
{
this.Activated(this, args);
}
}
/// Raises the Game.Deactivated event. Override this method to add code to handle when the game loses focus.
/// The Game.
/// Arguments for the Game.Deactivated event.
protected virtual void OnDeactivated(object sender, EventArgs args)
{
if (this.Deactivated != null)
{
this.Deactivated(this, args);
}
}
/// Raises an Game.Exiting event. Override this method to add code to handle when the game is exiting.
/// The Game.
/// Arguments for the Game.Exiting event.
protected virtual void OnExiting(object sender, EventArgs args)
{
if (this.Exiting != null)
{
this.Exiting(null, args);
}
}
private void DoPaint(object sender, EventArgs e)
{
if (graphics.SynchronizeWithVerticalRetrace)
{
this.DrawFrame();
}
}
/// Resets the elapsed time counter.
public void ResetElapsedTime()
{
this.forceElapsedTimeToZero = true;
this.updatesSinceRunningSlowly1 = 0x7fffffff;
this.updatesSinceRunningSlowly2 = 0x7fffffff;
}
/// Updates the game's clock and calls Game.Update and Game.Draw.
public void Tick()
{
if (!this.exitRequested)
{
if (!this.isActive)
{
Thread.Sleep((int)this.inactiveSleepTime.TotalMilliseconds);
}
this.clock.Step();
this.elapsedRealTime = this.clock.ElapsedTime;
this.lastFrameElapsedRealTime += this.clock.ElapsedTime;
TimeSpan elapsedAdjustedTime = this.clock.ElapsedAdjustedTime;
if (elapsedAdjustedTime < TimeSpan.Zero)
{
elapsedAdjustedTime = TimeSpan.Zero;
}
if (this.forceElapsedTimeToZero)
{
this.elapsedRealTime = this.lastFrameElapsedRealTime = elapsedAdjustedTime = TimeSpan.Zero;
this.forceElapsedTimeToZero = false;
}
this.gameTime = new GameTime(
this.clock.CurrentTime, this.elapsedRealTime,
this.gameTime.TotalGameTime, this.gameTime.ElapsedGameTime,
this.gameTime.IsRunningSlowly
);
if (elapsedAdjustedTime > this.maximumElapsedTime)
{
elapsedAdjustedTime = this.maximumElapsedTime;
}
if (this.isFixedTimeStep)
{
this.accumulatedElapsedGameTime += elapsedAdjustedTime;
long num = this.accumulatedElapsedGameTime.Ticks / this.targetElapsedTime.Ticks;
this.accumulatedElapsedGameTime = TimeSpan.FromTicks(this.accumulatedElapsedGameTime.Ticks % this.targetElapsedTime.Ticks);
this.lastFrameElapsedGameTime = TimeSpan.Zero;
if (num == 0L)
{
return;
}
TimeSpan targetElapsedTime = this.targetElapsedTime;
if (num > 1L)
{
this.updatesSinceRunningSlowly2 = this.updatesSinceRunningSlowly1;
this.updatesSinceRunningSlowly1 = 0;
}
else
{
if (this.updatesSinceRunningSlowly1 < 0x7fffffff)
{
this.updatesSinceRunningSlowly1++;
}
if (this.updatesSinceRunningSlowly2 < 0x7fffffff)
{
this.updatesSinceRunningSlowly2++;
}
}
this.drawRunningSlowly = this.updatesSinceRunningSlowly2 < 20;
while (num > 0L)
{
num -= 1L;
try
{
this.gameTime = new GameTime(this.gameTime.TotalRealTime, this.gameTime.ElapsedRealTime,
this.totalGameTime, targetElapsedTime, this.drawRunningSlowly);
this.Update(this.gameTime);
}
finally
{
this.lastFrameElapsedGameTime += targetElapsedTime;
this.totalGameTime += targetElapsedTime;
}
}
}
else
{
TimeSpan span3 = elapsedAdjustedTime;
this.drawRunningSlowly = false;
this.updatesSinceRunningSlowly1 = 0x7fffffff;
this.updatesSinceRunningSlowly2 = 0x7fffffff;
try
{
this.lastFrameElapsedGameTime = span3;
this.gameTime = new GameTime(this.gameTime.TotalRealTime, this.gameTime.ElapsedRealTime,
this.totalGameTime, this.lastFrameElapsedGameTime, false);
this.Update(this.gameTime);
}
finally
{
this.totalGameTime += span3;
}
}
if (!graphics.SynchronizeWithVerticalRetrace)
{
this.DrawFrame();
}
}
}
private void UnhookDeviceEvents()
{
if (this.graphicsDeviceService != null)
{
this.graphicsDeviceService.DeviceCreated -= new EventHandler(this.DeviceCreated);
this.graphicsDeviceService.DeviceResetting -= new EventHandler(this.DeviceResetting);
this.graphicsDeviceService.DeviceReset -= new EventHandler(this.DeviceReset);
this.graphicsDeviceService.DeviceDisposing -= new EventHandler(this.DeviceDisposing);
}
}
protected virtual void UnloadGraphicsContent(bool unloadAllContent)
{
}
/// Called when graphics resources need to be unloaded. Override this method to unload any game-specifc graphics resources.
protected virtual void UnloadContent()
{
}
/// Called when the game has determined that game logic needs to be processed. Override this method with game-specific logic.
/// Time passed since the last call to Microsoft.Xna.Framework.Game.Update(Microsoft.Xna.Framework.GameTime).
protected virtual void Update(GameTime gameTime)
{
for (int i = 0; i < this.updateableComponents.Count; i++)
{
this.currentlyUpdatingComponents.Add(this.updateableComponents[i]);
}
for (int j = 0; j < this.currentlyUpdatingComponents.Count; j++)
{
IUpdateable updateable = this.currentlyUpdatingComponents[j];
if (updateable.Enabled)
{
updateable.Update(gameTime);
}
}
this.currentlyUpdatingComponents.Clear();
this.doneFirstUpdate = true;
}
private void UpdateableUpdateOrderChanged(object sender, EventArgs e)
{
IUpdateable item = sender as IUpdateable;
this.updateableComponents.Remove(item);
int index = this.updateableComponents.BinarySearch(item, UpdateOrderComparer.Default);
if (index < 0)
{
index = ~index;
while ((index < this.updateableComponents.Count) && (this.updateableComponents[index].UpdateOrder == item.UpdateOrder))
{
index++;
}
this.updateableComponents.Insert(index, item);
}
}
/// Gets the collection of GameComponents owned by the game.
/// The collection of game GameComponents.
public GameComponentCollection Components
{
get
{
return this.gameComponents;
}
}
/// Gets or sets the time to sleep when the game is inactive.
/// The time to sleep when the game is inactive.
public TimeSpan InactiveSleepTime
{
get
{
return this.inactiveSleepTime;
}
set
{
if (value < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException("Resources.InactiveSleepTimeCannotBeZero", "value");
}
this.inactiveSleepTime = value;
}
}
/// Indicates whether the game is currently the active application.
/// true if the game is active; false otherwise.
///
public bool IsActive
{
get
{
return this.isActive;
}
}
internal bool IsActiveIgnoringGuide
{
get
{
return this.isActive;
}
}
/// Gets or sets a value indicating whether to use fixed time steps.
/// true if using fixed time steps; false otherwise.
///
public bool IsFixedTimeStep
{
get
{
return this.isFixedTimeStep;
}
set
{
this.isFixedTimeStep = value;
}
}
///
/// Gets or sets a value indicating whether the mouse cursor should be visible.
///
/// true if the mouse cursor should be visible; false otherwise.
public bool IsMouseVisible
{
get
{
return this.isMouseVisible;
}
set
{
this.isMouseVisible = value;
}
}
/// Gets the GameServiceContainer holding all the service providers attached to the Game.
///
/// The GameServiceContainer holding all the service providers attached to the Game.
///
public GameServiceContainer Services
{
get
{
return this.gameServices;
}
}
/// Gets or sets the target time between calls to Game.Update when Game.IsFixedTimeStep is true.
/// The target time period for the game loop.
public TimeSpan TargetElapsedTime
{
get
{
return this.targetElapsedTime;
}
set
{
if (value <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException("Resources.TargetElaspedCannotBeZero", "value");
}
this.targetElapsedTime = value;
}
}
///
/// Gets the current Framework.Graphics.GraphicsDevice.
///
///
/// The current Framework.Graphics.GraphicsDevice.
///
public GraphicsDevice GraphicsDevice
{
get
{
IGraphicsDeviceService graphicsDeviceService = this.graphicsDeviceService;
if (graphicsDeviceService == null)
{
graphicsDeviceService = this.Services.GetService(typeof(IGraphicsDeviceService)) as IGraphicsDeviceService;
if (graphicsDeviceService == null)
{
throw new InvalidOperationException("NoGraphicsDeviceService");
}
}
return graphicsDeviceService.GraphicsDevice;
}
}
public GraphicsDeviceManager graphics;
/// Raised when the game gains focus.
public event EventHandler Activated;
/// Raised when the game loses focus.
public event EventHandler Deactivated;
/// Raised when the game is exiting.
public event EventHandler Exiting;
private TimeSpan accumulatedElapsedGameTime;
private GameClock clock;
private List currentlyDrawingComponents;
private List currentlyUpdatingComponents;
private bool doneFirstUpdate;
private List drawableComponents;
private bool drawRunningSlowly;
private TimeSpan elapsedRealTime;
private bool exitRequested;
private bool forceElapsedTimeToZero;
private GameComponentCollection gameComponents;
private GameServiceContainer gameServices;
private GameTime gameTime;
private IGraphicsDeviceManager graphicsDeviceManager;
private IGraphicsDeviceService graphicsDeviceService;
private GameHost host;
private TimeSpan inactiveSleepTime;
private bool inRun;
private bool isActive;
private bool isFixedTimeStep;
private bool isMouseVisible;
private TimeSpan lastFrameElapsedGameTime;
private TimeSpan lastFrameElapsedRealTime;
private readonly TimeSpan maximumElapsedTime;
private List notYetInitialized;
private TimeSpan targetElapsedTime;
private TimeSpan totalGameTime;
private List updateableComponents;
private int updatesSinceRunningSlowly1;
private int updatesSinceRunningSlowly2;
}
} // namespace Nuclex