Today we will talk about the next step in my project “Odyssey” game series. We have already talked about why we need to design a game, and then I attempted to design my own game which I call “Odyssey”. Now that we know what we’re trying to build, it’s time to prepare our “workbench” with the tools we need! One of the coolest samples we have released is the Game State Management sample. This sample gives you a working template you can use to create game menus, options, pop up messages, etc. I will be using this sample in project “Odyssey” to handle all the menus and game options. Trust me, this is saving me a whole lot of time! Let’s see how this sample works.
So what does the Game State Management (GSM) sample do exactly? I am going to steal the description text from the sample page on creators.xna.com:
The sample implements a simple game flow with a main menu, an options screen, some actual gameplay, and a pause menu. It displays a loading screen in between the menus and gameplay, and uses a popup message box to confirm whether the user really wants to quit.
I am going to do even better! Here’s a video of what the sample actually looks like, it will give you a much clearer picture of how it works:
See how it works? The sample has all the boiler plate code required to give your game menus, options, pause screen, etc. You just need to figure out how to use it and where to place your game code as well as any customizations to the interface.
GSM Sample Code Structure
The sample is structured in a very object oriented way so that you can just plug into it and get up and running quickly. To start things off, here’s the class diagram of the sample (Click on it to see the full thing):
Alright, keep this diagram opened in a window since we’ll refer to it quite often.
How does it work?
The basic principle behind the sample is that it manages screens. A screen is a renderable entity in your game. You break down your game into screens and let the ScreenManager manage them for you by bringing them into view and out based on what the player does. Best way to understand how the screens idea works is to go through a scenario step by step.
Let’s go through the scenario of the player booting up the game, they get a Menu screen that has a Play option. They click on that and they see a loading screen and then they see the actual game play. They play a bit and then they hit Esc to quit and are presented with a “Are you sure you want to quit?” message. They say Yes and the game quits back to the main menu. This would require you to break up your scenario into a bunch of screens like this:
So the flow would be:
Menu -> Play game -> Loading Game -> Game play -> Pause Game -> Quit Game option -> Confirm quit -> Back to main menu
The would break up to the following screens:
- Main Menu Screen: This is the screen that renders the Menu. Here you would have all the code that draws the options, moves selections up and down, etc:
- Loading Screen: This is the screen that renders while the game is loading it’s content. In the sample, it looks like this: (very creative as you can see)
- Gameplay Screen: This is the screen that contains all your game play. So if this was say SpaceWar, this would be the screen where the ships fight each other for instance. In the sample, we see this screen:
- Pause Screen: When you hit the Esc button, you get to the pause screen which is a screen that renders on top of another one (i.e. Popup). This means that unlike the other screens we saw so far, it doesn’t remove the previous screen but instead dims it and renders over it:
- Quit Game Screen: This is the confirmation screen for when you choose the “Quit Game” option. Again, this is a popup screen that renders on top of all the other screens dimming them under it.
There you go! When you confirm that you do indeed want to quit the game, you go back to the Main Menu screen and you are back at square one :)
Looking at the source
Alright, so now that we somewhat understand how this whole screens thing works, let’s take a closer look at the code. First we’ll start with that layout of the code in source explorer:
As you can see, the code is split into two main folders, the ScreenManager and Screens.
ScreenManager folder contains the classes that are instrumental in how the template works:
GameScreen.cs: This is the base GameScreen class that all your screens will inherit from. The class has a lot of interesting methods and properties, let’s take a closer look at some of them:
- bool isPopup: You want to set this to true if you want to have your screen be drawn on top of the current screen. Otherwise, it will be transitioned in as the previous screen is transitioned away. Screens like Pause, confirmation boxes, etc will want this to be true. Screens like the loading screen, game screen, etc will want it false.
- Timespan transitionOnTime and transitionOffTime: This is how you can control how fast this screen will transition in and out. You want to set this using something like:
this.transitionOffTime = TimeSpan.FromSeconds(2);
- ExitScreen(): This is the function you want to call when your screen is exiting. This will have it transition off the correct way and get removed from the list of screens to be managed.
- LoadContent(): Override this function and add code to load any content you will be using in this screen. Do the same for UnloadContent() of course.
- HandleInput(): You want to override this function in your class and do all the input handling here. Why not do it in the Update? Well, this function will be called when your screen is Active and is in focus. This way the right screen gets to handle the input at the right time.
- Draw(): Override this function and add your rendering code.
InputState.cs: Helper for reading input from keyboard and gamepad. This class tracks both the
current and previous state of both input devices, and implements query properties for high level input actions such as “move up through the menu” or “pause the game”.
This class is really helpful and is the one that you should extend to handle your own input as well. The class exposes methods that translate the gamePad input to more appropriate game actions. For instance, things like IsMenuSelect method can be used in the Menu screen to detect if the user clicked on the A button or Enter key to make a selection. Here, check out how it is used in the code for the menus in the sample:
1: public override void HandleInput(InputState input)
2: {
3: // Move to the previous menu entry?
4: if (input.MenuUp)
5: {
6: selectedEntry--;
7:
8: if (selectedEntry < 0)
9: selectedEntry = menuEntries.Count - 1;
10: }
11: }
See how it is very easy to handle input in a meaningful to the game way? No need to do code that directly checks for the A button press, instead, it is wrapped in the InputState class and the action for Menu Select is defined as a check for the A button press. My game will extend this class to handle things like input.MoveShipLeft for instance. Read the source for InputState.cs to get a better idea for how it’s used. Very well commented.
ScreenManager.cs: Ah
, we come to the mother of all classes! This class right here is the heart of how this sample works! The screen manager is the component which manages one or more GameScreen instances. It maintains a stack of screens, calls their Update and Draw methods at the appropriate times, and automatically routes input to the topmost active screen (text shameless stolen directly from the source).
So how does it work? Simple. You create your GameScreen drived screens (Menu, game play, pause, etc) and use the AddScreen method to add them to the ScreenManager. The ScreenManager simply iterates over all the screens it had, calls update on each one, calls InputHandle to the top most one and finally renders them all with the top most one rendered last.
So if you add a GamePlay screen followed by a Pause screen and then a Quit Confirmation screen, you’ll get all three screens rendered with the top most one (Confirmation one) in “focus” and the only one handling the input. You RemoveScreen (which you do by calling the ExitScreen() method) the confirmation screen and the Pause screen is now the top most one and is receiving input.
See how it all works now? It’s a stack of screens that are basically layers. Top most layer gets input and the rest don’t. Brilliant!
Every GameScreen has a ScreenManager property on it pointing to the one and only ScreenManager that was created at the start of the game. This way you can add/remove screens to it from any screen in the game.
Tip: Notice the SpriteBatch property on the ScreenManager? That is put there for convenience. Makes is easy to share one SpriteBatch across your game. You won’t need to create a new one in your screen to draw something. Just use this one instead!
Now we move on to the Screens folder to see what’s in there.
This folder contains the actual implementation of the sample around the template mechanics we discussed above. As you can see, there are a few screens here. There are a few that are specially interesting to us:
- GamePlayScreen.cs: This is the screen that the sample uses to render the “game play” portion. So if you want to instantly use this sample for your game, just move your game code in this file and hook it up appropriately. It will just work and all you need to do is customize the menus and stuff and you’re done!
- LoadingScreen.cs: Alright, this one is very important and needs you to pay attention to how it works because it’s a bit different than the others. The purpose of the LoadingScreen is to handle transitions between the game and the menu system. That does not include the menus that popup during the game play though. Let me explain further. When you start the sample, you start by adding a Background screen and then a Main Menu screen. Once you are ready to start the game, you technically can just add the GamePlay screen to the ScreenManager and it will work. But this will mean that you will have both the background and menu screens sitting in memory for no reason. So instead, you use the LoadingScreen as follows:
LoadingScreen.Load(ScreenManager, true, new GameplayScreen());
What this will do is the following: It will first clear all the screens that are in the ScreenManager because we don’t need to worry about them anymore. It then will render a Loading screen image as the GamePlayScreen is loading its content. You don’t have to do anything special for that. The Loading screen will keep the Loading text or image you specify up on the screen until your content is loaded. Very nifty. If your GamePlayScreen’s content loading is very fast, you can disable the rendering of a Loading text by setting the second argument in the Load method to false.
Now, once your game is ready to go back to the main menu screen, you will realize that that screen is no longer there! We cleared it when we loaded the game. So to go back to that screen, the sample uses the LoadingScreen again to transition us back to the Menu system like this:
LoadingScreen.Load(ScreenManager, false, new BackgroundScreen(),
new MainMenuScreen());
See how that worked? We are telling the LoadingScreen to clear the current ScreenManager (it does that in the Load method) and then load it up with the BackgroundScreen followed by the MainMenuScreen. We use false for the second argument since we know that our MainMenuScreen loads pretty fast.
So as you can see, the LoadingScreen is instrumental as it sits in between your Main Menu and the game. You use it to transition from one to the other and back. You don’t have to use it and can just stack screens in the ScreenManager, but then if you have a particularly beefy Main Menu screen, you’ll have those resources in memory for no reason. Use it!
As for the rest of the classes up there, take a look at MessageBoxScreen for instance to see how it uses the this.isPopup = true to indicate that it is a pop up window that will not transition the ones below it.
Oh, you should check out MainMenuScreen.cs and OptionMenuScreen.cs to see how they use MenuEntry class to implement the actual menu items. It uses events to detect when a menu was clicked. It’s simple enough to understand from the code.
Final words and next steps
Phew! That was a loooong post eh? Sorry about that but I really felt like I needed to make sure that this sample is fully explained before we can proceed. I myself haven’t really used it in depth, so this was a good exercise for me too. Once you go through how it works and look at the code a little bit, it totally makes sense very quickly and becomes trivial to use. It is intimidating at first but hopefully my explanation will help remove that.
Going forward we’ll start thinking about how to implement and structure project “Odyssey” in preparation for the implementation phase! Looks like I will have to actually code up this game! Should be fun and educational too!
In the meantime, go ahead and download the game state sample now and start playing with it. Get a sample game or even a moving texture integrated in it (Tip: Put the code you already have in the GamePlayScreen.cs file for starters). You will learn to love this sample quickly.
Thanks for reading! I'd love to hear your thoughts, feel free to leave a comment below. Don't forget to subscribe to my RSS Feed!
Hello,
I have a project and I decided to merge it with this Gamestate sample to add some menu functionality to my game.
On my Gameplay screen I have a few things that should be Initialized first but I can’t initialize them by using the Initialize() method like I did before I merged the project with my game.
Do I have to add my initialization method somewhere else? maybe the gamescreen or screen manager?
Thanks