XNA Tool GUI

Introduction

The original intent of this project was to create a GUI for XNA to allow a user to visually paramertize parts of a Game/Graphics application during runtime. There are many uses for a tool window while developing. A Console window overlay can allow for user input to change variables during runtime, but this requires knowing commands and is not visually intuitive. I began writting the XNA Tool GUI to replace much of the tweaking that I would have done from a console window before. XNA Tool GUI is designed to allow the XNA graphics application to run while tweaking parameters so that the user gets immediate visual feedback.

Some uses, although not all inclusive, are tweaking of shader parameters, AI logic variables, model changes, lighting values, and menu systems for object placement and properties.

This project was not originally targeted for in game menu systems, but could be easily modified to add XBox360 controller input.

The GUI subsystem is not multithreaded and does not rely heavily on delegates (Events). All input can be gathered during Update processing, although some of the controls do use delegates to provide immediate feedback if needed, such as Buttons.

Implementation

Rather than have the GUI library manage all input, I decided to let the application do it. A Single static class manages the GUI subsystem, XnaGUIManager. Calls to XnaGUIManager.Update(...) allows the GUI subsystem to check for activation and focus, when the mouse is visible (see, Game.IsMouseVisible in the XNA documentation). The XnaGUIManager.Update(...) method will activate the GUI when the mouse is clicked on a GUI control (all GUI elements are controls), and will deactivate the GUI if the mouse is clicked on an area of the screen that does not contain a GUI control.

All GUI elements are derrived from XGControl, and all controls have a list of child controls. There are two types of controls that were created to be topmost controls (windows?), XGTabControl and XGPanel. XGTabControl hosts XGTabPage controls that contain a collection of controls related by subject matter according to your design.

Note: There are some controls that expect a particular control type to be their parent or child. Examples are, XGTabControl expects it's children to be XGTabPage controls and vise-versa, and XGRadioGroup expects it's children to be XGRadioButton controls and vise-vera.

Getting Started

There are four main requirements to get a GUI window up and running.
  • Create the GUI controls (you might create a class for this to keep logic for each tool window type seperate)
  • Call XnaGUIManager.Update( float frameTime ) in your Games Update method
  • Call XnaGuiManager.Draw( float frameTime ) in your Games Draw method
  • Set Game.IsMouseVisible = true to enable the GUI subsystem
You can disable the GUI subsystem (hide tool windows) by setting Game.IsMouseVisible = false, even though XnaGUIManager's Update and Draw methods are still being called.

Remember that all coordinates for controls are relative to their parent. For the topmost control, or tool window, use screen coordinates, but all controls parented by other controls use relative coordintates.

Example Code

    /// <summary>
    /// ToolWindow is an example of an Xna Tool GUI window
    /// </summary>
    public class ToolWindow : XGControl
    {
        public Game1 Game;
        public XGTabControl TabControl { get; protected set; }
        public XGTabPage PageOne { get; protected set; }

        public ToolWindow(Game1 game)
            : base(new Rectangle(2, 2, 220, 250), true)
        {
            Game = game;

            // Add our window to the Controls to be managed by the XNA GUI manager

            XnaGUIManager.Controls.Add(this);

            // Offset the Tab Control and Tab Page rectangles relative to this Tool Window 

            Rectangle pageRect = this.Rectangle; // tab pages are the size of the tab control
            pageRect.X = pageRect.Y = 0;  // but use parent relative coordintates

            // Create the Tab Control

            TabControl = new XGTabControl(pageRect);
            Children.Add(TabControl); // add the tab control to our child control list

            // Create the first tab page (ToolPage class)

            XGTabPage PageOne = new ToolPage(game, pageRect);
            TabControl.Children.Add(PageOne);
        }
    }

    /// <summary>
    /// ToolPage is an XGTabPage that displays various controls
    /// </summary>
    public class ToolPage : XGTabPage
    {
        public Game1 Game;
        public XGTextBox TextBox1 { get; protected set; }
        public XGLabeledSlider Slider1 { get; protected set; }
        public XGLabel Slider1Label { get; protected set; }
        public XGTree Tree { get; protected set; }

        public ToolPage(Game1 game, Rectangle rect)
            : base(rect, "Tool Page")
        {
            Game = game;

            // Add controls to the tab page

            Children.Add(new XGLabel(new Rectangle(10, 10, 200, 20), "Hello world")); // leave default Left/VCenter alignment
            Children.Add(new XGButton(new Rectangle(10, 34, 66, 20), "Click Me", this.Button_Clicked));
            TextBox1 = new XGTextBox(new Rectangle(80, 34, 130, 20));
            Children.Add(TextBox1);

            // Create a slider with a label on the left and a value label on the right

            Slider1 = new XGLabeledSlider(new Rectangle(10, 66, 200, 20), 34, "Bkg:", 34, 0.0f, 0.0f, 1.0f);
            Children.Add(Slider1);

            // Add a tree control and initialize it with a few tree items

            Tree = new XGTree(new Rectangle(10, 100, 200, 100), TreeItemClicked);
            Children.Add(Tree);

            Texture2D goldButtonTex = game.Content.Load<Texture2D>(@"Textures\GoldButton16x16");
            Texture2D greenButtonTex = game.Content.Load<Texture2D>(@"Textures\GreenButton16x16");

            for (int i = 0; i < 3; i++) // add 3 items
            {
                XGTreeItem topItem = new XGTreeItem(string.Format("Item {0}", i), goldButtonTex);
                Tree.Items.Add(topItem);

                for (int n = 0; n < 3; n++) // add 3 child irems to each top item
                {
                    XGTreeItem childItem = new XGTreeItem(string.Format("Item {0}.{1}", i, n), greenButtonTex);
                    topItem.Items.Add(childItem);
                }
            }

            Tree.ExpandAll();
        }

        // Button click event handler
        void Button_Clicked(XGControl sender)
        {
            TextBox1.Text = "Button was clicked";
        }

        // Tree item click handler
        void TreeItemClicked(XGTreeItem item)
        {
            TextBox1.Text = item.Value.ToString() + " clicked";
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            // Game.BackgroundColor must be public in Game1 and used to clear the backbuffer

            Game.BackgroundColor = Color.Lerp(Color.Black, Color.CornflowerBlue, Slider1.Value);
        }
    }

This code (at the time of writing) produced the following tool window

XNAToolGUI_ExPic2.png

Last edited Sep 21, 2010 at 3:40 AM by ClydeCoulter, version 26

Comments

No comments yet.