The menu system is a whole different beast that I need to tame. Many months ago I downloaded the Game State Management code from the official XNA code samples, and found much of it helpful, if a bit too much for my needs. The code sample organized everything in “screens”, and any different game mode in it is a screen, whether it’s a loading screen, a menu, or a gameplay screen.
I found it odd that the menu should be a screen itself, and to me it made more sense to separate the Menu objects from the concept of the screen. So over time, I reworked the code so instead of screens, it just deals with the menus and the interactions from the user. In my game, I use a game state system completely different from the XNA sample, where each game state is a class derived from an abstract GameState object. This system doesn’t use stacks at all and just runs one state at a time. The game tells the current state to update itself, and the state is responsible to change the current state of the game when needed.
Menus are just an optional thing that the game states can have, and each one has a MenuManager (altered from the sample’s ScreenManager) in its base class. Those menus only belong to that particular GameState. It’s also possible to have multiple menus in each class, and the menu events can tell other menus to load. This feature is close to the one in the sample code, and it’s the one I chose to keep the most. Menu events can also tell the current GameState to replace itself with a different one, but that’s as far as it goes to being connected with them.
Getting the menus to play nice with the system I had already in place was a bit of chore. They wouldn’t communicate with all the objects very well, and I had some crashes where the game state would change, but failed to load the resources for the menus because the Content Manager wasn’t set. I also didn’t like the fact that it communicated with the Game object for a lot of things. Long story short, I changed the menu manager so it only needed the content and graphics services, which is all it really needs to begin with.
Skins for my menus
Finally, a more interesting feature comes along: menu skins. This is all custom-made feature I made just to mess around with the look of the menus. The original menus use SpriteFont to write the text to the screen. A menu skin class looks for an XML file with a particular name, loads it and parses the data to determine the SpriteFont, colors, and sprite dimensions of each text item.
This is all well and good, but I wanted to make it a bit more flashy. In each menu entry, I saved the text to a render target texture, then when it comes to draw the complete menu, draw all the textures to separate quads. Now you can add sorts of crazy effects to the text through shaders, having them activate when one is selected. But that STILL wasn’t enough for me 😛 Afterwards I applied a perspective projection before drawing the quads, so you can now render the menus with a 3D perspective! Here is a very exciting screenshot with a sample menu:
I know, I desperately need some artistic assets, but I hope the picture is clear enough to show what it does. I intentionally added solid rectangles to make the perspective effect easier to see, but by default the background behind the text is transparent. So to recap, here’s what the menu system does from the presentation side:
- Loads the XML file to determine style properties of the menu
- Sets a render target for each menu item
- Renders the text, and then off to the next item
- After all items are done, a perspective projection is applied
- All the menu item textures are drawn to different quads
- This menu itself can be saved to its own render target texture, so it’s separate from whatever else is on the screen
That’s a lot of texturing and drawing to do. Right now it’s not optimized to re-use textures, or use texture atlases/primitive batching etc, but I will get to that eventually. For now it works fine for basic menus containing a small number of items, which is fine by me. One thing I want to point out is that the menu item text is not set in the XML. I intentionally left it this way to separate content with presentation.