65.9K
CodeProject 正在变化。 阅读更多。
Home

Managed DirectX 应用程序的 GUI 库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (17投票s)

2006 年 8 月 14 日

4分钟阅读

viewsIcon

177107

downloadIcon

4939

本文介绍 Odyssey User Interface,这是一个可以在任何托管 DirectX 应用程序中使用的控件库。

Screenshot - OdysseyUI.jpg

引言

我正在开发一款名为《星际奥德赛》的 4X 太空歌剧游戏,继承了《大师的奥德赛》系列的传统。每个游戏都需要一个用户界面,我的也不例外。DirectX SDK 提供了一个看起来非常酷的示例 UI,但很难从中提取项目,而且它依赖于纹理 GUI 元素。由于我不擅长绘制 GUI 元素,所以走这条路会花费很长时间。相反,我采取了“程序员艺术”的方式:以动态方式创建控件和其他 GUI 元素,同时尽力使其看起来很酷。结果在此呈现。读者应该对 DirectX 术语有一定的经验。由于游戏不需要绝对的 FPS 响应能力,因此使用简单的 Windows 消息来处理输入。请注意,要运行示例,您需要安装 DirectX SDK。如果您遇到问题,请尝试使用DirectX Web 安装程序

使用代码

此项目中提供的控件设计得就像 Windows Forms 控件一样。因此,那些已经有开发 Forms 应用程序经验的人会发现这些可渲染控件非常容易使用。在演示存档中,您会找到 Odyssey UI.dll。它可以被任何 C# MDX 项目引用,并且是使用此库的最简单方法。否则,您可以编译源代码。要在您的代码中使用该库,您必须遵循以下简单步骤

  1. 引用 AvengersUTD.Odyssey.UserInterface 命名空间。
  2. 您必须让 Odyssey UI 管理 Windows Forms 事件。为了方便您,您可以调用提供的 `UI.SetupHooks(form);` 方法。
  3. 将您的 DirectX 设备对象分配给 UI.Device。在整个代码中,有几个“设备”调用,因此这避免了您每次调用都传递设备引用。
  4. 创建一个 HUD 对象。HUD 是用户界面的屏幕覆盖。
  5. 告诉 HUD 您正在开始设计 UI,`hud.BeginDesign();`。
  6. 像编写 Windows Forms 应用程序时一样创建控件。
  7. 将控件添加到 HUD 容器中,`hud.Add(control);`。请注意,HUD 的顶层子项应被理解为位于桌面上的内容。因此,避免它们重叠是您的责任。如果您想要多个窗口,请将控件添加到一个窗口内,然后将该窗口添加到 HUD 中。
  8. 如有必要,分配事件。
  9. 完成 UI 创建后,使用 `hud.EndDesign();`。
  10. 将这样创建的 HUD 对象分配为用户界面的 CurrentHUD,`UI.CurrentHUD = hud;`。这允许您指定不同的 HUD 并轻松切换 HUD
  11. 最后,在您的渲染循环中,您只需调用 `hud.Render();`。

以下是渲染图像中显示的界面的示例代码

// Add these lines where appropriate in your code:
// (provided that you did all the above steps also)

UI.Device = yourDirectXDevice;
UI.SetupHooks(form);

// Create a new hud object. The string parameter
// is its ID: it will be used in the future
// when the library will allow users
// to "skin" via xml the user interface.
// ScreenSize refers to a size variable that represents
// your current resolution. The HUD should always be set
// to be as big as it can be.

HUD hud = new HUD("TestHud", Settings.ScreenSize);

private void TestUI()
{
    // Start designing the UI
    hud.BeginDesign();

    // Create a panel control;
    Panel panelTest = new Panel("UI Test Panel", 
        new Vector2(25, 150), new Size(520, 400));

    // Create two labels. The Vector2 parameter
    // refers to its position in the parent control.
    // Since the labels are going to be
    // added to the previous panel, the
    // absolute position of the first
    // one will then be: (X: 25 + 20, Y: 250 + 20).
    // The alignment parameters are used
    // to specify how you want to draw the text.
    // The first label can be highlighted, hence the 
    // extra color value in the constructor.
    
    Label lTest = new Label("LabelTestPanel", 
                  "The blue window is a Panel" + 
                  " Control and this is a Label",
                  Alignment.Left, Alignment.Top, 
                  new Vector2(10, 10), Color.White, Color.Red);

    Label lTb = new Label("LabelTrackBar", "This TrackBar control " + 
                "goes from 0 to 10 with a\n'TickFrequency' value of 2",
                Alignment.Left, Alignment.Top, 
                new Vector2(20, 40), Color.LightGreen);

    // This is a button. We have attached a delegate
    // to its "MouseClick" event: it will be fired
    // when the user clicks on the button.
    
    Button example = new Button("ButtonExample", "This is a button", 
        new Vector2(20, 320), new Size(200, 50));
    example.MouseClick += delegate(BaseControl sender, 
        System.Windows.Forms.MouseEventArgs e)
    {
        example.Label = "Yep you clicked me";
    };
    
    // This button opens a modal dialog box.
    // In windows forms the MessageBox.Show method is "blocking"
    // it means that the program does not continue to the 
    // "next line" until the user presses one of the dialog's buttons.
    // Since simulating that behavior would have taken too much time
    // for the time being you have pass a delegate method as a parameter
    // that tells what you want to do. Just pass null if you simply
    // want the window to close.
    
    Button dialogTest = new Button("DialogTest", "Show me a dialog", 
        new Vector2(300,320), new Size(200,50) );
    dialogTest.MouseClick += 
        delegate(BaseControl sender, 
        System.Windows.Forms.MouseEventArgs e)
    {
        DialogBox.Show("Test", 
            "Do you like this User Interface?\n\nBy the way, " + 
            "this dialog is modal!",
            DialogBoxButtons.YesNo,
            delegate(BaseControl ctl, DialogResult dialogResult)
        {
            if (dialogResult == DialogResult.No)
                DialogBox.Show("Really?", 
                "Sigh.... :(", DialogBoxButtons.Ok, null);
            else
                DialogBox.Show("Thanks", 
                "I'm glad that you liked it!\n\nIf you have any " + 
                "feedback drop me a line at avengerdragon at gmail.com " + 
                "or visit my forum at " + 
                "[hover=\"Aquamarine\"]http://starodyssey.avengersutd.com[/]",
                DialogBoxButtons.Ok, null);
        });
    };

    // This is a trackbar control. You have to set
    // the trackbar minim value, tick Frequency
    // and maximum values with the SetValues method.
    // You can also attach a delegate method for
    // its "ValueChanged" event.
    
    TrackBar slider = new TrackBar("TrackBar", 
        new Vector2(20, 100), new Size(200, 30));
    slider.SetValues(0, 2, 10);
    slider.ValueChanged += delegate(BaseControl ctl) {
        lTb.Text = "The trackbar value is now: " + slider.Value;
    };

    // This is a textbox. When you click on it, you can start typing.
    
    TextBox tb = new TextBox("TextBox",
        new Vector2(20, 140), new Size(200, 30), 24);
    tb.Text = "This is a textbox";

    // This is a groupbox control: a simple panel
    // with a flat border and a caption.
    
    GroupBox gb = new GroupBox("GroupBox", "This is a groupbox", 
        new Vector2(20, 200), new Size(200, 100),
        Color.White, BorderStyle.Flat);

    // This is an OptionGroup control: a collection of radio buttons.
    // The string array passed as a parameter
    // will be used to dynamically create an option button
    // for each of the strings. You can also attach
    // a delegate too its "SelectedIndexChanged" event
    
    OptionGroup og = new OptionGroup("OptionGroup", new string[] {
        "This is", "the OptionGroup", "control"}, 
        new Vector2(5, 10), new Size(100, 30));
    og.SelectedIndexChanged += delegate(BaseControl ctl)
    {
        gb.Caption = "You clicked the OptionButton number: " + 
            og.SelectedIndex;
    };
    
    // This is the CheckBox Control. You can access its 
    // selected value through its .IsSelected property.
    
    CheckBox cb = new CheckBox("CB1", "Checkbox", 
            new Vector2(300, 300), new Size(100,30));

    // This is the DropDownList control also known as
    // a combobox.
    
    DropDownList ddl = new DropDownList("DDL",
        new string[] { "This", "is the", "DropDownList", "control" },
        new Vector2(300, 100), new Size(150, 30));    

    // Next, we'll  create three windows.
    // We'll then create three controls to go inside those 
    // windows: a RichTextArea, a Table and a TabPanel
    
    Window win1 = new Window("win1", 
        "Test Window #1", new Vector2(500, 100), new Size(640, 480));
    Window win2 = new Window("win2", 
        "Test Window #2", new Vector2(550, 110), new Size(640, 480));
    Window win3 = new Window("win3", 
        "Test Window #3", new Vector2(600, 120), new Size(640, 480));
    
    // This is the RichTextArea control. It is a panel that 
    // automatically formats input depending on BBCode like
    // string. We simply pass the string to be formatted and the
    // style to use as default (if the default one is Arial 20pt 
    // for example, the bold command will apply the bold effect
    // on the Arial 20pt font)
    // Accepted commands are b,i,s for bold, italic and shadowd
    // respectively and c or color for the standard color and
    // h or hover for the color to use when the mouse pointer
    // is over the label. Nested markup is not supported at
    // the moment.
    // We pass an empty vector because we want it to cover
    // the whole internal area of the window. We manually
    // compute its size because there's no "autosize" feature yet.
    
    RichTextArea rta = new RichTextArea("RTA", 
        Vector2.Empty, new Size(632, 300), rtext, TextStyle.Default);
        
    // This is the TabPanel control. A Panel that has some
    // buttons on the top that allow the user to access different
    // pages in it. Each page in this example has a label control.
    // You can switch page by clicking the top buttons.
    
    TabPanel tabPanel = new TabPanel("Tab", 
        new Vector2(30, 50), new Size(310, 200));
    tabPanel.AddTab("Page 1");
    tabPanel.AddControlInTab(new Label("pag1", 
        "Page 1", Alignment.Left, Alignment.Top,
        new Vector2(100, 20), Color.LightGreen), 0);
    tabPanel.AddTab("Page 2");
    tabPanel.AddControlInTab(new Label("pag2", 
        "Page 2", Alignment.Left, Alignment.Top,
        new Vector2(100, 20), Color.LightGreen), 1);
    tabPanel.AddTab("Page 3");
    tabPanel.AddControlInTab(new Label("pag3", 
        "Page 3", Alignment.Left, Alignment.Top,
        new Vector2(100, 20), Color.LightGreen), 2);    
        
    // This is the table control. You can esaily format by choosing
    // different TableStyle parameters
    
    Table table = new Table("Table", 3, 2, new Vector2(15, 15));
    table[0, 0].Text = "This";
    table[0, 1].Text = "is the";
    table[1, 0].Text = "Table";
    table[1, 1].Text = "control!";
    table.Format(new TableStyle(150, 30, 1, 1, Border.All));
            
    // Finally we add each control to its parent container.
    
    gb.Add(og);
    panelTest.Add(lTest);
    panelTest.Add(lTb);
    panelTest.Add(slider);
    panelTest.Add(tb);
    panelTest.Add(gb);
    panelTest.Add(ddl)
    panelTest.Add(example);
    panelTest.Add(dialogTest);
    
    win1.Add(rta);
    win2.Add(tabPanel)
    win3.Add(table);
    
    hud.Add(panelTest);
    hud.Add(win1);
    hud.Add(win2);
    hud.Add(win3);
    
    // Signal the hud object that we're doing creating controls
    
    hud.EndDesign();
}

// After having called the TestUI method
// somewhere in yor app before rendering,
// in your render loop you have to place
// the following line:

public void Render()
{
    hud.Render();
}

关注点

我试图模仿 Windows Forms 事件样式,结果非常相似。在我开发游戏的过程中,它非常有帮助,因为一旦所需的控件实现并完全正常工作,您就可以专注于游戏本身,从而加快进程。这些是 UI 的主要功能

  • 完整的**窗口**支持:UI 正确渲染多个窗口。
  • 模态和**可拖动**窗口。
  • 形状支持:内置圆形、矩形和梯形默认形状,但您可以通过 `Graphics` 静态类定义新的形状。
  • 边框样式:平面、凸起、凹陷;还可以实现更多样式。
  • 控件状态更改:启用、高亮、聚焦、选中等。
  • 所有您能想到的**鼠标**和**键盘**事件!

以下控件包含在内

  • Window
  • DialogBox
  • Label
  • Panel
  • Button
  • 文本框
  • TrackBar
  • GroupBox
  • OptionGroup
  • CheckBox
  • TabPanel
  • 表格
  • DropDownList
  • RichTextArea
  • PictureBox

值得注意的是,通过派生或改进 PictureBox 控件,您理论上可以开发一套全新的纹理控件,以及可渲染的控件。您甚至可以组合它们来实现新的效果。有多种可能性。更多控件正在开发中。要及时了解此库的开发进展,请参阅星际奥德赛网站上的项目页面。请在论坛上发布您发现的任何错误和请求。

Odyssey UI 在知识共享署名-非商业性许可下发布。您可以随意修改此源代码,如果您决定在您的项目中使用它,请告诉我,以便我们可以共享链接(如果您愿意)。感谢您的阅读!

历史

  • 2007 年 7 月 22 日:版本 0.3 - 添加了 Window、DialogBox、RichTextArea 和 PictureBox 控件。
  • 2007 年 6 月 14 日:版本 0.2 - 更新版本。添加了 Table、Tabpanel 和 Checkbox 控件以及初步的图层功能。
  • 2006 年 8 月 14 日:版本 0.1 - 首次提交 CodeProject。
© . All rights reserved.