[.net] Gametime class in a windows app

Started by
17 comments, last by remigius 17 years, 2 months ago
Decided to clean up the code a bit and make it a bit more organize and easier to use. If you have any questions let me know.

#region Using Statementsusing System;using System.Collections.Generic;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Audio;using Microsoft.Xna.Framework.Content;using Microsoft.Xna.Framework.Graphics;using Microsoft.Xna.Framework.Input;using Microsoft.Xna.Framework.Storage;#endregionnamespace XNACore{    /// <summary>    /// This is the main type for your game    /// </summary>    public class Game1 : Microsoft.Xna.Framework.Game    {        GraphicsDeviceManager graphics;        ContentManager content;        private System.Windows.Forms.Form form;        private Microsoft.Xna.Framework.Graphics.Viewport xnaViewport;        #region Left Panel Controls        private System.Windows.Forms.Panel panelLeft;        private System.Windows.Forms.Button button1;        #endregion        #region Right Bottom Panel Controls        private System.Windows.Forms.Panel panelRightBottom;        #endregion        public Game1()        {            graphics = new GraphicsDeviceManager(this);            content = new ContentManager(Services);                    }        /// <summary>        /// Allows the game to perform any initialization it needs to before starting to run.        /// This is where it can query for any required services and load any non-graphic        /// related content.  Calling base.Initialize will enumerate through any components        /// and initialize them as well.        /// </summary>        protected override void Initialize()        {            // TODO: Add your initialization logic here            base.Initialize();            Init_Viewport();            Init_Windows_Controls();        }        /// <summary>        /// Load your graphics content.  If loadAllContent is true, you should        /// load content from both ResourceManagementMode pools.  Otherwise, just        /// load ResourceManagementMode.Manual content.        /// </summary>        /// <param name="loadAllContent">Which type of content to load.</param>        protected override void LoadGraphicsContent(bool loadAllContent)        {            if (loadAllContent)            {                // TODO: Load any ResourceManagementMode.Automatic content            }            // TODO: Load any ResourceManagementMode.Manual content        }        /// <summary>        /// Unload your graphics content.  If unloadAllContent is true, you should        /// unload content from both ResourceManagementMode pools.  Otherwise, just        /// unload ResourceManagementMode.Manual content.  Manual content will get        /// Disposed by the GraphicsDevice during a Reset.        /// </summary>        /// <param name="unloadAllContent">Which type of content to unload.</param>        protected override void UnloadGraphicsContent(bool unloadAllContent)        {            if (unloadAllContent == true)            {                content.Unload();            }        }        /// <summary>        /// Allows the game to run logic such as updating the world,        /// checking for collisions, gathering input and playing audio.        /// </summary>        /// <param name="gameTime">Provides a snapshot of timing values.</param>        protected override void Update(GameTime gameTime)        {            // Allows the default game to exit on Xbox 360 and Windows            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)                this.Exit();            // TODO: Add your update logic here            base.Update(gameTime);        }        /// <summary>        /// This is called when the game should draw itself.        /// </summary>        /// <param name="gameTime">Provides a snapshot of timing values.</param>        protected override void Draw(GameTime gameTime)        {            graphics.GraphicsDevice.Clear(Color.Blue);                // TODO: Add your drawing code here                        base.Draw(gameTime);        }        private void Init_Viewport()        {            xnaViewport = new Viewport();            xnaViewport.X = this.Window.ClientBounds.Width / 2;            xnaViewport.Y = 0;            xnaViewport.Width = this.Window.ClientBounds.Width / 2;            xnaViewport.Height = this.Window.ClientBounds.Height / 2;            this.graphics.GraphicsDevice.Viewport = xnaViewport;        }        private void Init_Windows_Controls()        {            // Get the windows form based on the Game Window            IntPtr ptr = this.Window.Handle;            form = (System.Windows.Forms.Form)System.Windows.Forms.Control.FromHandle(ptr);            #region Left Panel Init            panelLeft = new System.Windows.Forms.Panel();            panelLeft.Width = this.Window.ClientBounds.Width / 2;            panelLeft.Height = this.Window.ClientBounds.Height;            panelLeft.Top = 0;            panelLeft.Left = 0;            form.Controls.Add(panelLeft);            #endregion            #region Left Panel Controls Init            button1 = new System.Windows.Forms.Button();            button1.Text = "It works!!!";            button1.Top = 20;            button1.Left = 20;            panelLeft.Controls.Add(button1);            #endregion            #region Left Panel Controls Event Handlers            button1.Click += new EventHandler(button1_Click);            #endregion            #region Right Bottom Panel Init            panelRightBottom = new System.Windows.Forms.Panel();            panelRightBottom.Width = this.Window.ClientBounds.Width / 2;            panelRightBottom.Height = this.Window.ClientBounds.Height / 2;            panelRightBottom.Top = this.Window.ClientBounds.Height / 2;            panelRightBottom.Left = this.Window.ClientBounds.Width / 2;            form.Controls.Add(panelRightBottom);            #endregion            #region Right Bottom Panel Control Init            #endregion            #region Right Bottom Panel Controls Event Handlers            #endregion        }        void button1_Click(object obj, EventArgs ea)        {            System.Windows.Forms.MessageBox.Show("See, told you it works");        }    }}


Not sure if it is what you wanted, but I tried. :)

Anyone's comments on this idea are much appriciated.

theTroll
Advertisement
Thanks a million bud! It worked great.
Glad it was what you needed. If you have any questions, let me know.

theTroll
Don't mean to be the moralizing party-pooper, but Shawn Hargreaves blogged about this last week and he recommended not using the Game class for rendering into Windows Form controls:

Quote:Just say no! If you want to render into a custom window, don't use the Game class. It will be much easier and more reliable to directly create your own GraphicsDevice object.


He goes on to point out some source code on how to create your own IGraphiscDeviceService to bind your content manager to and how to set up your own render loop. I've followed these easy instructions and with a bit of tinkering you can set up a framework that should allow your game to run both in your Windows Forms editor as well as through the Game class without any recoding. One thing to change in the example code is dropping the Timer based render loop and use the good ol' AppStillIdle loop instead to make better use of your CPU resources.

Unfortunately neither Shawn nor the sample code go into fetching the GameTime for your updates, but I've worked around this by setting up 2 System.Diagnostics.Stopwatch objects as timers. One will never be stopped and measures the real time part of the GameTime. The other timer will be paused as the game is paused, which makes up the actual game time part of the GameTime object. In case anyone's wondering, the Stopwatch mimics the DirectX framework timer (using QueryPerformanceCounter) and hence has a better time resolution than the system TickCount.

The only thing I'm worrying about is that with this approach you will need to create a new GameTime object every frame, since the class does not seem to expose methods or properties for updating the existing object. Ideally I'd like to keep a single GameTime object around to update every frame and pass on into the underlying components. I've thought about just passing in the timespans (or just seconds as floats), but I'd like to stick as close to the original XNA way of doing things as possible.


Edit: just checked with Reflector and the setter for the GameTime properties are indeed internal. I've created a MS Connect feedback item to have these changed to 'protected internal', so we could create our own updatable GameTime subclass, without exposing the undesirable behavior of being able to change normal GameTime object. Please vote for the report here if you agree [smile]

[Edited by - remigius on January 29, 2007 3:48:23 AM]
Rim van Wersch [ MDXInfo ] [ XNAInfo ] [ YouTube ] - Do yourself a favor and bookmark this excellent free online D3D/shader book!
Quote:Original post by remigius
Don't mean to be the moralizing party-pooper, but Shawn Hargreaves blogged about this last week and he recommended not using the Game class for rendering into Windows Form controls:

Quote:Just say no! If you want to render into a custom window, don't use the Game class. It will be much easier and more reliable to directly create your own GraphicsDevice object.


He goes on to point out some source code on how to create your own IGraphiscDeviceService to bind your content manager to and how to set up your own render loop. I've followed these easy instructions and with a bit of tinkering you can set up a framework that should allow your game to run both in your Windows Forms editor as well as through the Game class without any recoding. One thing to change in the example code is dropping the Timer based render loop and use the good ol' AppStillIdle loop instead to make better use of your CPU resources.

Unfortunately neither Shawn nor the sample code go into fetching the GameTime for your updates, but I've worked around this by setting up 2 System.Diagnostics.Stopwatch objects as timers. One will never be stopped and measures the real time part of the GameTime. The other timer will be paused as the game is paused, which makes up the actual game time part of the GameTime object. In case anyone's wondering, the Stopwatch mimics the DirectX framework timer (using QueryPerformanceCounter) and hence has a better time resolution than the system TickCount.

The only thing I'm worrying about is that with this approach you will need to create a new GameTime object every frame, since the class does not seem to expose methods or properties for updating the existing object. Ideally I'd like to keep a single GameTime object around to update every frame and pass on into the underlying components. I've thought about just passing in the timespans (or just seconds as floats), but I'd like to stick as close to the original XNA way of doing things as possible.


Edit: just checked with Reflector and the setter for the GameTime properties are indeed internal. I've created a MS Connect feedback item to have these changed to 'protected internal', so we could create our own updatable GameTime subclass, without exposing the undesirable behavior of being able to change normal GameTime object. Please vote for the report here if you agree [smile]



remigius, I am guessing you either didn't read my code or you didn't understand what I did. I am NOT using the Game class for rendering into Windows Form controls. But wait you said you are using the Game class and windows controls together? Well yes I did. I first went to add the Game class and GameWindow to a standard windows application and fairly quickly realized that would be a very bad idea. So I started playing around with some ideas trying to figure out an easy way to build a game designer. I realized instead of putting a Game class into a windows app we could use windows controls onto a GameWindow. We keep all the functionality of the Game class, without any hacks and add the ease of using windows controls. I don't have to create my own gameloop, I don't have to create a new GameTime every frame and I don't have to create my own IGraphiscDeviceService. Now there might be a major down side to this that I am missing but so far I have not seen it.

theTroll
I'm just working on a custom XNA render control myself so I thought I'd chime in. Perhaps I was a bit overzealous though, for which I apologize.

Anyway, I did realize you were reusing the existing Form that's constructed by the Game class, but I think Shawn's advice applies here as well. With your solution you are getting the correct rendering size, but I think (please correct me if I'm wrong) you're losing quite some performance since the backbuffer is still set to the full size of the Form. You also don't have too much control over your form, device creation and render loop, which was a downside for me.

Obviously this all doesn't need to be a huge problem, but since the alternative is easy enough I went with that. With one of the XNA platform programmers advocating it, it can't be too much of a hack. The only thing that's indeed a bit of a problem is the GameTime thingy... So please vote [smile]
Rim van Wersch [ MDXInfo ] [ XNAInfo ] [ YouTube ] - Do yourself a favor and bookmark this excellent free online D3D/shader book!
I was not offended but thanks for the appology anyway. :)

I see your point about the backbuffer, but I thought that by using a viewport that issue would be avoided, I could be completely wrong on that. So I was thinking about what you said and decided that I really didn't know enough to make the decission on if this is a good idea or not.

So I sent Shawn Hargreaves an email, telling him my idea and asking what were the problems with doing it this way. I will let you know if/when I get a response.

theTroll
Just got a response from Shawn. He said the way that I am doing it is quite feasable. Now with that being said, he said, by not using the GraphicsDevice and writing my own loop I will be limiting some of what is possible doing it the other way. I specifically asked about performace issues and he never said that there would be any doing it this way.

Just wanted to update folks on what he said.

theTroll
Odd, I recall reading a discussion that did mention some performance issues. Ah well, there's no arguing with Shawn [smile] Thanks for sharing the feedback!
Rim van Wersch [ MDXInfo ] [ XNAInfo ] [ YouTube ] - Do yourself a favor and bookmark this excellent free online D3D/shader book!

This topic is closed to new replies.

Advertisement