NeHe Tutorial #1

13. April 2010 13:48 by Jeromy Walsh in Tutorials  //  Tags:   //   Comments (13)

As I stated in my previous post, over the next couple of weeks I'm going to be working my way through ALL 48 of the NeHe OpenGL tutorials (in addition to some of my own) in an effort to both show how easy it is to use XNA, as well as to highlight some of the differences between XNA 3.1 and XNA 4.0.

To get us off to the right start, it's necessary to create a "framework" for our applications. This is a basic "game" which we can add to in later demos in order to minimize the amount of work required. Fortunately for us, XNA does most of this for us with the XNA Game API.  XNA already handles creation of the main game window, the creations of the graphics device, and the update and draw phases of the main game loop. The only things we need to add to the starting game to put us in line with the NeHe tutorials are:

  • The ability to toggle between fullscreen and windowed mode
  • The ability to see the mouse when in windowed mode
  • The ability to resize the window and handle projection accordingly

So let's get started!

To begin the process of creating the sample demo we just need to launch XNA Game Studio 4.0. This is where we see the first difference. The whole thing is navy blue and .NET-looking. This is because, as you probably noticed during installation, XNA Game Studio 4.0 is built on Visual C# 2010 Express Edition. The new look you see when developing with XNA GS 4.0 is from the newer version of Visual C#. Another thing which was immediately obvious to me was the list of references. As you can see from the image below, XNA Game Studio 4.0 includes a lot more assemblies. More to the point, XNA 4.0 breaks the XNA Framework Library into mutiple, smaller assemblies. This makes it possible for you to completely remove assemblies from the project which you're not going to be using, thus reducing the loading time and the number of assemblies loaded during game execution.

This also makes it easier for the developers. XNA is a cross-platform framework, and by breaking the API into multiple assemblies it allows them to factor out the platform-specific implementations more carefully, while allowing assemblies which are compatible across multiple platforms to remain intact. I should note, I'm not sure Microsoft is actually doing this, as a number of classes in Xna.Framework probably still require multiple implementations. For example, Xna.Framework still contains all of the audio classes (not including the Xact stuff), as well as all of the input classes (not including the new Touch library). So while Xna.Framework.dll could conceptually only contain classes which are shared across all platforms, I doubt that's the case....yet.  Moving on.

Another thing you'll likely notice from the screenshot is the way Content Projects are now handled. In Xna 3.1 content projects were embedded/sub-projects of the current project. Now they're isolated projects with "Content References" from the game projects. I'm not sure all of the implications of this yet, but I'll explore it more in a few weeks when I start delving into the content pipeline more aggressively. For now, it's just an observation.

Ok, with the project/file structure stuff out of the way, let's look at some code. This is the code for our starting game, which I will use as the basis for all additional NeHe style demos.

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

As with before, XNA Game Studio likes to include every XNA namespace as well as the kitchen sink when starting a new project. However, in an effort to limit the scope, I removed the namespaces I'm not going to need here. Incidentally, this means you can remove some of the referenced assemblies as well. Don't forget to do that!

namespace JWalsh.XnaSamples
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        KeyboardState prevKeyboardState = Keyboard.GetState();

The next thing you see is the usual namespace, the Game1 class definition, and a few fields. You'll notice that I've added a KeyboardState right away. I've done this because the demos/tutorials in this series will all focus (initially) on the PC. I'll be re-adding some WinPhone 7 and Xbox 360 versions at a later date as well. Anyways... we'd like to be able to toggle between fullscreen and windowed mode, as well as hit ESC in order to exit the program. At least one of them requires a previous state. We can ignore previous state when the user hits ESC.

Two things you'll notice missing from the Game class are the previously deprecated LoadGraphicsContent and UnloadGraphicsContent methods. Yup, that's right. If you were still using those out-dated methods it's time to make the switch, because your code isn't going to compile otherwise.

Next, XNA continues to put the SpriteBatch member field in Game1 for you, and they are happy to initialize it in LoadContent as well, however I've removed it for these demos. When we get to the point we need sprites, I'll re-add it. Until then, no reason to have it around.

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            IsMouseVisible = true;

            // Allow users to resize the window, and handle the Projection Matrix on Resize
            Window.Title = "JWalsh's XNA Demo";
            Window.AllowUserResizing = true;
            Window.ClientSizeChanged += OnClientSizeChanged;
        }

After the fields is the constructor. Not much has changed here. I still create a GraphicsDevice in order to ensure the proper functioning of the ContentManager, and to make sure my Draw phase works correctly, and I also set the RootDirectory on the ContentManager as normal.

The following four lines are the unique elements of this demo. This ensures the mouse is visible, gives the app a friendly title, allows the window to be resized, and then captures any OnClientSizeChanged events in order to allow us to change the projection matrix used by our 3D math to accommodate the new aspect ratio. I won't be adding that in this demo, but it'll be there in the next.

After that I'm going to add a helper property and our event handler.

        protected bool IsFullScreen
        {
            get { return graphics.IsFullScreen; }
            set
            {
                if (value != graphics.IsFullScreen)
                {
                    // Toggle FullScreen, and Mouse Display, then apply the changes
                    // on the DeviceManager
                    graphics.IsFullScreen = !graphics.IsFullScreen;
                    IsMouseVisible = !IsMouseVisible;
                    graphics.ApplyChanges();
                }
            }
        }

        protected void OnClientSizeChanged(object sender, EventArgs e)
        {
        }

This property just allows us to easily toggle between fullscreen and windowed mode.  After that is the blank OnClientSizeChanged method. Nothing to see here folks.

Then we've got a few (more or less) empty methods.

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
        }

        protected override void UnloadContent()
        {
        }

As with previous versions of XNA, you must call base.Initialize() in the Initialize method if you want LoadContent to be called. In this demo it's not a huge necessity. But in all additional demos it'll be essential that LoadContent be called. After those functions we have the Update method. This method is one of the few methods that had any modifications to it for this project.       

        protected override void Update(GameTime gameTime)
        {        
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            KeyboardState keyboard = Keyboard.GetState();
         
            if (keyboard.IsKeyDown(Keys.Escape) )
                Exit();

            if (keyboard.IsKeyDown(Keys.F11) && prevKeyboardState.IsKeyUp(Keys.F11))
                IsFullScreen = !IsFullScreen;

            prevKeyboardState = keyboard;
        }

In Update, we capture the Back button of the Game Pad as usual, so we can close the application with the Game Pad. In addition, we get the keyboard state so we can check to see whether ESC is down, or whether the F11 key was pressed. Note: By checking to see if F11 is down NOW, but wasn't PREVIOUSLY, it avoids the nasty situation of changing back and forth between fullscreen and windowed mode a bunch of times because you held the F11 key down too long.

Another thing to note about Update is that while it still receives a GameTime object, the GameTime object itself has changed. Noticeably absent are the ElapsedRealtime and TotalRealTime properties. Presumably, the difference between game time and real time was confusing enough to developers that they did away with wall clock time. Now, holding the window by dragging the titlebar around, or resizing the window causes the game to stop processing. When you release the window, it continues updating as though no time at all has elapsed. For those who still want access to the wall-clock time, you've still got the DateTime class, which gives millisecond precise times.

And finally we come to the Draw method. In this demo the only thing the Draw method does is clear the back buffer. Again, nothing to see here folks!

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
        }
    }
}

If you enter in the above, and/or download the full project file below and compile/run it, you'll see the following, unexciting screen:

Hopefully this brief look at the starting project and some of the changes to the API have been interesting. In the next project we'll starting drawing some triangles to the screen.

Download Project 1

Comments (13) -

Justin Self
Justin Self
11/16/2010 8:04:48 PM #

In my app,

base.Update(gameTime);

was already set in my Update method and

base.Draw(gameTime);

was in my Draw method; I noticed they are not in yours. When I comment them out, I noticed my window border did not flicker (it was unattractive, so thats a good thing) when resizing. Is this an untold benefit? Why did you choose to leave that out?

Chaos Following
Chaos Following
1/30/2011 3:12:33 AM #

Leaving it out means that the base Game class doesn't update or draw. The biggest problem with this(that I've noticed) is that GameComponent and DrawableGameComponents won't update or draw if you leave out the code in question.

Jeromy Walsh
Jeromy Walsh
1/30/2011 9:41:44 AM #

Chaos is correct. It primarily prevents the Game class from trying to process its GameComponent and DrawableGameComponent lists. I left it out because I'm not using components in this example. A lot of people throw them in because the documentation generally shows them. But why include them if you don't need them?

Johnny
Johnny
11/24/2010 5:01:10 AM #

wow! I've been looking for this. I'm an aspiring Game Developer and I decided to study XNA. Thanks and great blog! Smile

Greystone
Greystone
12/29/2010 12:38:03 PM #

Wow... basically for a TUTORIAL you have the DEFAULT xna 4.0 framework, that COMES with the thing.
This did not help at ALL, as the thing does this on it's OWN.

Greystone
Greystone
12/29/2010 12:39:04 PM #

only thing you really did was "RENAME" the window... I saw no other differences.

pothb
pothb
1/22/2011 5:06:02 AM #

He's explaining things...

*facepalm*

Nick
Nick
1/31/2011 2:46:54 AM #

You sir, are an idiot.

biteschlessen
biteschlessen
12/29/2010 12:50:03 PM #

That's not true. If you press F11 the window resizes.

GiXXeR
GiXXeR
1/17/2011 11:52:31 AM #

Hey, nice i'll be following this tutorials!

newuser
newuser
1/19/2011 8:56:53 AM #

Well I gave it a try (never been using neither xna nor c# before, so I'm generally a bit lost), but it didn't work this way for me..
I got it running using an implemented method  graphics.ToggleFullScreen() though. description says: "toggles between full screen and windowed mode".

simply add this line in standard framework's update method for the same result:

if (keyboard.IsKeyDown(Keys.F11) && prevKeyboardState.IsKeyUp(Keys.F11)) graphics.ToggleFullScreen();

greetings

Rob
Rob
2/2/2011 3:30:45 PM #

My guess is that you forgot to initialize the IsMouseVisible variable to true in the constructor method.  Do that and you should be fine.

public Game1()  

02 {  

03     graphics = new GraphicsDeviceManager(this);  

04     Content.RootDirectory = "Content";  

05     IsMouseVisible = true;  

dyork11
dyork11
9/10/2011 6:06:58 PM #

The series looks good, so following right along...

All fine so far.

Pingbacks and trackbacks (31)+

About the author

Jeromy Walsh is a professional game programmer with multiple credited and uncredited AAA game titles. Jeromy's primary area of expertise is in Tools/Engine development, though he likes to fiddle with other areas as well.

Month List