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