Thursday, February 08, 2007

A general GameScreen implementation

A few weeks ago, Eli posted a design for a GameScreen and a GameScreenManager to use in XNA. The GameScreen is a base class, like Game, that implements one layer of a game (e.g. the game itself, or a menu, or a title screen). The GameScreenManager is used by the Game class to switch between these screens using a stack-based design. I ended up implementing this design for my book, and I've been using a few weeks with no problems so I thought I'd post it here for the XNA community to use. To use the GameScreenManager, declare a public instance of it on your Game object, along with any screens that are spawned by Game and not by other screens. public GameScreenManager screenManager; // Screens StartMenuScreen menuscreen; PlayScreen play; public Game1() {

... screenManager = new GameScreenManager(2); ...
} Add the first screen during Game.Initialize() by creating the screen, initializing it, and calling GameScreenManager.Push. I also create the other screen at this point, but I don't initialize it until it's invoked. protected override void Initialize() {
// TODO: Add your initialization logic here play = new PlayScreen(this); play.Initialize(); screenManager.Push(play); menuscreen = new StartMenuScreen(this); ...
} At some point later, if the menu is invoked, I initialize it and push it over top of the play screen: // if the menu isn't already up if (screenManager.Peek() != menuscreen) { GamePad.SetVibration(PlayerIndex.One, 0, 0); // install menu screen menuscreen.Initialize(); screenManager.Push(menuscreen); Lastly, when the menu is finished, I fire an event back Game, which removes it. public void MenuScreenFinished(int Selection) {
screenManager.Pop(); if (Selection == 0) return; if (Selection == 1) { play.ResetGame(); } if (Selection == 2) { this.Exit(); }
}
You can download the GameScreen.cs and a sample screen here. Also, I should probably hawk the fact that my book is available for pre-order.

Labels: ,

Friday, January 05, 2007

XNA's LoadGraphicsContent API

A friend of mine was a little confused about the best way to use XNA's LoadGraphicsContent API, so I thought I'd try to clarify it a bit here. In general, your XNA Game object (or DrawableGameComponent object) will experience the following calls:

  1. Constructor
  2. Initialize()
  3. LoadGraphicsContent()
  4. Update / Draw repeatedly
  5. UnloadGraphicsContent()
  6. (maybe) goto 3
  7. Dispose()

In your constructor, you don't want to do much of anything. Assign values to things that aren't allowed to be null later, and maybe cache some constructor parameters into private fields of your object. Constructors in C# as a rule should avoid throwing exceptions. Try not to call any system APIs or anything that uses a resource, since that's a good way to encounter an exception. You can and should set your graphics preferences on the graphics object here.

In Initialize you have a lot more freedom. Your graphics object has been initialized and you should have a valid device. This is a good place for all the object creation and system calls that you wanted to make in your constructor. Don't load any Textures or Models yet though.

Load your Textures and Models in LoadGraphicsContent instead. This will get called every time the graphics card is reset (e.g. if the application moves from one monitor to another) and if you load a Texture in Initialize you'll be in for a nasty surprise after the device resets and you didn't reload it in LoadGraphicsContent. This is also where you want to create new RenderTargets.

UnloadGraphicsContent gets called when the device is being reset, or your application is ending. If you've loaded anything that needs explicit disposal, do that here. You'll notice the boilerplate telling the Content Manager to dump what it's loaded. If the device is being reset this call should be followed by a LoadGraphicsContent with the new graphics device settings.

You will rarely need to override Dispose. If you load something in Initialize that requires disposal you should do it here.

Update: One thing I forgot to mention about Initalize - if you are making a GameComponent, you don't want to make any assumptions about whether you are going to be drawn or receive updates soon. It could be a while between the time an application initializes a component and when it actually gets to update or draw. Some of the XNA documention samples do things in Initialize they probably shouldn't - for example, one of the audio samples plays a sound in Initialize (that gaffe in the docs is my fault, actually). While you can get away with that in a Game object it's a bad habit in general.

Update2: If you want to control when LoadGraphicsContent is called, call Initialize on your Game's base class.

Labels:

Thursday, January 04, 2007

How I spent my Christmas Vacation

I've been working feverishly every night and weekend on my book. The first step is to do the code for the second section of the book, that introduces 2D programming concepts. To illustrate sprite programming I've created a game I call "Velocity" where you pilot a spaceship through rings while avoiding asteroids and the treacherous gravity wells of planets. I'm not much of an artist, but here is a screenshot of the game working on the test map (I plan to make one decent map to ship with the book). Click on the graphic for the larger version. I may get a chance to improve the art before the book is finished (Mars and the asteroid Gaspra courtesy of NASA, but the ship and the ring are my fumbling attempt at art). I'm also doing 2 more versions of Velocity: one as a "2.5D" game and one as a 3D game from within the ship's cockpit.

Labels: ,