Friday, June 24, 2011

XNA Menus

Since December 2010 I have been working on and off again on an XNA Menu Framework. The project was born when I was working on a game for Game Makers Anonymous. We needed a bunch of menus, and since I didn't want to hack them together I started to design something more...

I have released an older version of this framework to the Game Makers Anonymous (club / company). It is being used in several of their active projects. But the more I was working with my system, the more I realized how much more I wanted to do with it. Here is a list of current functionality.


  • Parent child relationship menu items. Such properties as Position, Scale and Rotation will trickle to the children items.
  • Frames with layouts. (See example of this below)
  • Event driven customization on a menu item level. Such events the user can customize are
    • Any gamepad input
    • On focus / unfocus of items
  • Menu to menu flow with saved menu states. (Menu to Submenu hierarchy)
  • Complex behavior easily done with overridden items.(ie. Scroll lists of buttons, or what is known as an option button. (one button with many options that change on input)
Some of the more specific ideas I have in terms of features are click and motion handling for menu items. (for windows phone 7). If I ever add those to the framework or not is still undecided. The overall goal of the project is to create a very simple but powerful menu API.

This week I decided to fix my layout code to handles sizes correctly. I was stumped for a while on how I wanted to approach measuring the size of each menu item. I knew that I could measure the texture width on a button to get the size, but that doesn't account for scale. So if I scale it, I still need the unscaled version in a lot of scenarios. Then I realized this week I also need to know the size of an object that has been placed in a layout. That makes three different size types I had to juggle and nicely fit into my framework. After re-factoring and testing I finally was able to get it to work. I broke most of my more complex widgets doing this, but I think it will well be worth it in the end.

Here is some sample code of a Menu using a horizontal layout - applying alignments on each item. There is some more code that involves updating and drawing the menu, but that code is trivial to what I am trying to show.



   1:  public class MainMenu : Menu
   2:  {
   3:      MenuButton btn_one;
   4:   
   5:      /// <summary>
   6:      /// Makes the main menu for AnonymousMan
   7:      /// </summary>
   8:      public MainMenu(MultiController controllers, List<Controller> allControllers, Rectangle bounds)
   9:          : base(controllers, bounds)
  10:      {
  11:          //Main Menu Backgrounds
  12:   
  13:          btn_one  = new MenuButton(Frame, controllers, App.redButton, App.redButtonHighlight, "One");
  14:          MenuButton btn_two = new MenuButton(Frame, controllers, App.redButton, App.redButtonHighlight, "Two");
  15:          MenuButton btn_three = new MenuButton(Frame, controllers, App.redButton, App.redButtonHighlight, "Three");
  16:   
  17:          btn_one.HorizontalAlignment = MenuItem.HorizontalAlignmentType.Stretch;
  18:          btn_one.VerticalAlignment = MenuItem.VerticalAlignmentType.Bottom;
  19:          btn_one.LayoutStretch = 2;
  20:          btn_two.HorizontalAlignment = MenuItem.HorizontalAlignmentType.Right;
  21:          btn_two.LayoutStretch = 2;
  22:          btn_two.VerticalAlignment = MenuItem.VerticalAlignmentType.Top;
  23:          btn_three.VerticalAlignment = MenuItem.VerticalAlignmentType.Stretch;
  24:          btn_three.HorizontalAlignment = MenuItem.HorizontalAlignmentType.Right;
  25:          this.Frame.Layout = MenuFrame.LayoutType.Horizontal;
  26:   
  27:          btn_one.fontFocusColor = Color.Black;
  28:          btn_one.FontColor = Color.MidnightBlue;
  29:   
  30:  #if DEBUG
  31:          btn_one.Debug(Color.Gray, 5);
  32:          btn_two.Debug(Color.Blue, 5);
  33:          btn_three.Debug(Color.Green, 5);
  34:  #endif
  35:          Reset();
  36:      }
  37:  }
Output from sample Code.
The rectangles are a product of Debug code