GuiManager

The GUI manager is a DrawableGameComponent designed to make it easy to set up a GUI in a game. You can manage the GUI without a GUI manager to do some advanced things like rendering in-game computer displays, but using the GUI manager is much simpler and covers all aspects of the GUI a typical game will require.

BlackWindowsSkinGui.png

The GUI Manager automatically manages a screen (stores the state of a GUI) for you, sets up an input receiver (captures input and subclasses the window if run on the PC to capture WM_CHAR for country-specific keyboard input like accents and umlauts) and will create a default visualizer with a default skin for you if you don't specify your own.

How to Use

Here's a short guide explaining how to add the GUI to your game!

1. First, we need a field to store the GuiManager in and create a new instance of the GuiManager in our Game class' constructor. The !GuiManager instance can then be added to the GameComponents list to let it automatically draw itself (or you can call .Draw() and .Update() yourself, just like with any other GameComponent).
/// <summary>Demonstrates the capabilities of the Nuclex UserInterface library</summary>
public class UserInterfaceDemoGame : Microsoft.Xna.Framework.Game {

  /// <summary>Initializes a new instance of the user interface demo</summary>
  public UserInterfaceDemoGame() {
    this.graphics = new GraphicsDeviceManager(this);
    this.gui = new GuiManager(this);

    // You can either add the GUI to the Components collection to have it render
    // automatically, or you can call the GuiManager's Draw() method yourself
    // at the appropriate place if you need more control.
    Components.Add(this.gui);

    // It's a good idea to show the mouse if the user is supposed to click on
    // the buttons in the GUI :)
    IsMouseVisible = true;
  }

  // ...

  /// <summary>Manages the graphical user interface</summary>
  private GuiManager gui;

}

2. Now that we have the GUI ready, we should set up a Screen. A screen manages the GUI's state. You can have multiple screens if you have multiple separate GUIs (for example, if you write a first person shooter game with in-game computers showing a GUI), but in most cases, you will have only one screen. The GUI manages controls in a tree - any control can contain other controls. The root of this tree is the desktop control, belongs to the screen, is invisible and contains all other controls. We will resize the desktop control to 80% of the window in this example to prevent the GUI from going outside the title-safe area.
/// <summary>
///   Allows the game to perform any initialization it needs to before starting to run.
///   This is where it can query for any required services and load any non-graphic
///   related content. Calling base.Initialize will enumerate through any components
///   and initialize them as well.
/// </summary>
protected override void Initialize() {
      
  // Create a new screen. Screens manage the state of a GUI and its rendering
  // surface. If you have a GUI in your game window, you'd first create a screen
  // for that. If you have an in-game computer display where you want to use
  // a GUI, you can create another screen for that and thus cleanly separate
  // the state of the in-game computer from your game's main menu GUI :)
  Viewport viewport = GraphicsDevice.Viewport;
  Screen mainScreen = new Screen(viewport.Width, viewport.Height);
  this.gui.Screen = mainScreen;

  // Each screen has a 'desktop' control. This invisible control by default
  // stretches across the whole screen (all controls are positioned using both
  // a percentual position/size and absolute position/size). We change this to
  // prevent GUI or HUD elements from appearing outside the title-safe area.
  mainScreen.Desktop.Bounds = new UniRectangle(
    new UniScalar(0.1f, 0.0f), new UniScalar(0.1f, 0.0f), // x and y
    new UniScalar(0.8f, 0.0f), new UniScalar(0.8f, 0.0f) // width and height
  );

  base.Initialize();

}

3. Now we need something to show. You can directly add buttons and labels as children to the desktop control but that would be too easy, wouldn't it? We're going to create our own dialog, imitating the code created by the WinForms designer! Positioning and scaling in Nuclex.UserInterface works similar to CeGui: All coordinates have a fractional part and an offset. So if you want a control 50 pixels from the left border, use (as fraction + offset) 0% +50. If you want a control 50 pixels from the right border, use 100% -50. And if you want it in the center, use 50% +0. No complicated docking, anchoring and attaching required!
/// <summary>Dialog that demonstrates the capabilities of the GUI library</summary>
public partial class DemoDialog : WindowControl {

  /// <summary>Initializes a new GUI demonstration dialog</summary>
  public DemoDialog() {
    InitializeComponent();
  }

}

partial class DemoDialog {

  #region NOT Component Designer generated code

  /// <summary> 
  ///   Required method for user interface initialization -
  ///   do modify the contents of this method with the code editor.
  /// </summary>
  private void InitializeComponent() {
    this.helloWorldLabel = new Nuclex.UserInterface.Controls.LabelControl();
    this.okButton = new Nuclex.UserInterface.Controls.Desktop.ButtonControl();
    this.cancelButton = new Nuclex.UserInterface.Controls.Desktop.ButtonControl();
    //
    // helloWorldLabel
    //
    this.helloWorldLabel.Text = "Hello World! This is a label.";
    this.helloWorldLabel.Bounds = new UniRectangle(10.0f, 15.0f, 110.0f, 30.0f);
    //
    // okButton
    //
    this.okButton.Bounds = new UniRectangle(
      new UniScalar(1.0f, -180.0f), new UniScalar(1.0f, -40.0f), 80, 24
    );
    //
    // cancelButton
    //
    this.cancelButton.Bounds = new UniRectangle(
      new UniScalar(1.0f, -90.0f), new UniScalar(1.0f, -40.0f), 80, 24
    );
    //
    // DemoDialog
    //
    this.Bounds = new UniRectangle(100.0f, 100.0f, 512.0f, 384.0f);
    Children.Add(this.helloWorldLabel);
    Children.Add(this.okButton);
    Children.Add(this.cancelButton);
  }
	
  #endregion // NOT Component Designer generated code

  /// <summary>A label used to display a 'hello world' message</summary>
  private Nuclex.UserInterface.Controls.LabelControl helloWorldLabel;
  /// <summary>Button which exits the dialog and takes over the settings</summary>
  private Nuclex.UserInterface.Controls.Desktop.ButtonControl okButton;
  /// <summary>Button which exits the dialog and discards the settings</summary>
  private Nuclex.UserInterface.Controls.Desktop.ButtonControl cancelButton;

}

4. Believe it or not, that's it. Just add the dialog to your desktop control and it will be displayed and the user can interact with it using his mouse or keyboard. To be notified when a button is clicked, subscribe to its 'Clicked' event. For this example, we don't care about click events, we'll just add the dialog, as-is, to the desktop control:
/// <summary>
///   Allows the game to perform any initialization it needs to before starting to run.
///   This is where it can query for any required services and load any non-graphic
///   related content. Calling base.Initialize will enumerate through any components
///   and initialize them as well.
/// </summary>
protected override void Initialize() {

  // ...code from previous step...

  // Next, we add our demonstration dialog to the screen
  mainScreen.Desktop.Children.Add(new DemoDialog());

  base.Initialize();

}

Last edited Sep 28, 2009 at 7:58 AM by Cygon, version 3

Comments

No comments yet.