Wednesday, April 18, 2007

Tutorial 1

This first tutorial will cover setting up the GUIManager, adding new controls, and handling their events.

Setting Up The Project

The simplest way to begin is to use the GUI_Testbed project as a starting template. I simply created a new source file containing a class called Tutorial1, and set that as the startup Game object in Program.cs. You could also just use the existing GUI_Testbed.cs if you don't feel like starting from scratch.

Setting Up The GUIManager

The first thing to do is to add a couple using statements for the InputEventSystem and the XNAWindowSystem.


using System;
using Microsoft.Xna.Framework;
...
using InputEventSystem;
using XNAWindowSystem;


Now add some fields.


private GraphicsDeviceManager graphics;
private InputEvents input;
private GUIManager gui;


In the constructor we set up these objects, and add input and gui to the list of game components. It is important that the InputEvents object is created before the GUIManager object because it adds itself to the game services in the constructor, which the GUI will need access to. Not creating an InputEvents object at all will cause the program to crash.


public Tutorial1()
{
this.graphics = new GraphicsDeviceManager(this);

this.input = new InputEvents(this);
Components.Add(this.input);

this.gui = new GUIManager(this);
Components.Add(this.gui);

// GUI requires variable timing to function correctly
IsFixedTimeStep = false;
Window.Title = "XNA Window System Tutorial 1";
}


The GUIManager object should be initialised in the overriden Initialize() method, before any child controls are added. This is so that order of resource loading can be predicted.


protected override void Initialize()
{
// Has to be initialised before child controls can be added
this.gui.Initialize();

base.Initialize();
}


You will also want to override the Draw() method, to clear the screen each frame, although this probably doesn't affect the GUI as it handles drawing itself.

Adding Controls

I'm going to use a menu bar with various menus and menu items as an example. This will show how to create controls, how to add them to the GUI, and how to handle their events.

First we should add some fields, which are the menu bar and the menu items that the user will actually select. This means that we don't actually need to keep a reference to the 'File' menu for example, because it handles itself. We do need to keep references to child menu items that will actually be clicked, so that we can determine which one was selected. An example would be the 'New' or 'Save' menu items.


private MenuBar menuBar;
private MenuItem newMenuItem;
private MenuItem openMenuItem;
private MenuItem saveMenuItem;
private MenuItem saveAsMenuItem;
private MenuItem exitMenuItem;
private MenuItem undoMenuItem;
private MenuItem redoMenuItem;


Next we have to actually set up the GUI objects, and add them to the GUI. This should take place in Initialize(), after the GUIManager has been initialised.


this.menuBar = new MenuBar(this, gui);
MenuItem fileMenu = new MenuItem(this, gui);
fileMenu.Text = "File";
this.newMenuItem = new MenuItem(this, gui);
this.newMenuItem.Text = "New...";
fileMenu.Add(this.newMenuItem);
this.openMenuItem = new MenuItem(this, gui);
this.openMenuItem.Text = "Open...";
fileMenu.Add(this.openMenuItem);
this.saveMenuItem = new MenuItem(this, gui);
this.saveMenuItem.Text = "Save";
this.saveMenuItem.IsEnabled = false;
fileMenu.Add(this.saveMenuItem);
this.saveAsMenuItem = new MenuItem(this, gui);
this.saveAsMenuItem.Text = "Save As...";
this.saveAsMenuItem.IsEnabled = false;
fileMenu.Add(this.saveAsMenuItem);
this.exitMenuItem = new MenuItem(this, gui);
this.exitMenuItem.Text = "Exit";
fileMenu.Add(this.exitMenuItem);
menuBar.Add(fileMenu);
MenuItem editMenu = new MenuItem(this, gui);
editMenu.Text = "Edit";
this.undoMenuItem = new MenuItem(this, gui);
this.undoMenuItem.Text = "Undo";
this.undoMenuItem.IsEnabled = false;
editMenu.Add(this.undoMenuItem);
this.redoMenuItem = new MenuItem(this, gui);
this.redoMenuItem.Text = "Redo";
this.redoMenuItem.IsEnabled = false;
editMenu.Add(this.redoMenuItem);
this.menuBar.Add(editMenu);

// Add menubar to gui
this.gui.Add(this.menuBar);


You may notice that some of the menu items have a property called IsEnabled set to false. This basically makes the item grey and unclickable, just how windows does it. This property only applies to MenuItem objects currently, but at some point in the future, I will probably make it apply to all controls.

Now if you run the application, it should show a menu bar with all the new menus and menu items.

Control Events

Next we will add event handlers that will be called whenever a menu item is clicked. The reason for holding references to menu items is for comparison with the clicked control, determining which one was actually clicked.

So add the following code to the Initialize() method, probably after the menu bar is added to the GUI.


// Add event handlers
this.newMenuItem.Click += new ClickHandler(OnMenuItemClicked);
this.openMenuItem.Click += new ClickHandler(OnMenuItemClicked);
this.exitMenuItem.Click += new ClickHandler(OnMenuItemClicked);
this.undoMenuItem.Click += new ClickHandler(OnMenuItemClicked);
this.redoMenuItem.Click += new ClickHandler(OnMenuItemClicked);


Finally we need to add a new method that is called whenever a menu item is clicked. We simply check which item was clicked, and act accordingly.


private void OnMenuItemClicked(UIComponent sender)
{
if (sender == this.newMenuItem)
{
MessageBox messageBox = new MessageBox(
this,
gui,
"New clicked!",
"Tutorial 1",
MessageBoxButtons.OK,
MessageBoxType.Info
);
messageBox.Show(true);
}
else if (sender == this.openMenuItem)
{
MessageBox messageBox = new MessageBox(
this,
gui,
"Open clicked!",
"Tutorial 1",
MessageBoxButtons.OK,
MessageBoxType.Info
);
messageBox.Show(true);
}
else if (sender == this.undoMenuItem)
{
MessageBox messageBox = new MessageBox(
this,
gui,
"Undo clicked!",
"Tutorial 1",
MessageBoxButtons.OK,
MessageBoxType.Info
);
messageBox.Show(true);
}
else if (sender == this.redoMenuItem)
{
MessageBox messageBox = new MessageBox(
this,
gui,
"Redo clicked!",
"Tutorial 1",
MessageBoxButtons.OK,
MessageBoxType.Info
);
messageBox.Show(true);
}
else if (sender == this.exitMenuItem)
Exit();
}


This first tutorial was really simple, but should show you the basics of using the window system. It should be enough if all you want to do is add a couple buttons to your game. In the next tutorial I will show how to create a class that inherits from Window, to show how to begin to create a real application.

5 comments:

Mario said...

Awesome Aaron. I appreciate the help! I'll start hacking away on it!

shinjin said...

Awesome! Thank you for this library!

Anonymous said...

yar, it kicks butt!

Crsteinb said...
This comment has been removed by the author.
Crsteinb said...

Hi Aaron, interesting article, some good information. Check out my blog here, I have written a GUI Manager to work with the Hazy Mind engine. It is a bit different approach than you have used and you may find it interesting.
Keep up the good work and thanks for helping keep the community alive. It will be interesting to see what you come up with.