Introduction
I am developing a 4X Space Opera game called Star Odyssey, in the tradition of the Master of Orion series. Every game needs a User Interface and mine is no exception. The DirectX SDK provides a sample UI that looks very cool, but it is very difficult to extrapolate from the project and it relies on textured GUI elements. Since I am not very good at drawing GUI elements, it would have taken ages for me to go that way. Instead, I took the ''programmer art'' way: to create controls and other GUI elements in a dynamic way, trying my best to make it look cool at the same time. The result is presented here. Readers should be somewhat experienced in DirectX terminology. Since the game doesn't need absolute FPS responsiveness, simple Windows messages are used to handle input. Please note that in order to run the sample, you need the DirectX SDK installed. In case you have problems, try using the DirectX web installer.
Using the code
The controls provided in this project are designed to work just like Windows Forms controls. So, people that already have experience in developing Forms application will find these renderable controls very easy to use. In the demo archive, you'll find the Odyssey UI.dll. It can be referenced from any C# MDX project and it is the easiest way to use this library. Otherwise, you can compile the source code. To use the library in your code, you have to follow these easy steps:
- Reference the
AvengersUTD.Odyssey.UserInterface
namespace.
- You have to let Odyssey UI manage Windows Forms events. For your convenience, you can call the supplied method
UI.SetupHooks(form);
).
- Assign to
UI.Device
your DirectX device object. Throughout the code, there are several ''device'' calls, so this avoids you having to pass the device reference every call.
- Create a
HUD
object. The HUD is the on-screen overlay of the User Interface.
- Tell the HUD that you're starting to design the UI,
hud.BeginDesign();
.
- Create the controls as you would do when coding Windows Forms applications.
- Add the controls to the HUD container,
hud.Add(control);
. Note that the top level children of the HUD are to be understood as if they were on the desktop. So it is your responsibility to avoid making them overlap. If you want multiple windows, add the control inside a window and then add the window to the HUD instead.
- Assign events, if necessary.
- When you are done creating the UI, use
hud.EndDesign();
.
- Assign the
HUD
object created this way to be the CurrentHUD
of the User Interface, UI.CurrentHUD = hud;
. This allows you to specify different HUD
s and change HUD
with ease.
- Finally, in your render loop you just have to call
hud.Render();
.
The following is the sample code to render the interface you see in the image:
UI.Device = yourDirectXDevice;
UI.SetupHooks(form);
HUD hud = new HUD("TestHud", Settings.ScreenSize);
private void TestUI()
{
hud.BeginDesign();
Panel panelTest = new Panel("UI Test Panel",
new Vector2(25, 150), new Size(520, 400));
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);
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";
};
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);
});
};
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;
};
TextBox tb = new TextBox("TextBox",
new Vector2(20, 140), new Size(200, 30), 24);
tb.Text = "This is a textbox";
GroupBox gb = new GroupBox("GroupBox", "This is a groupbox",
new Vector2(20, 200), new Size(200, 100),
Color.White, BorderStyle.Flat);
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;
};
CheckBox cb = new CheckBox("CB1", "Checkbox",
new Vector2(300, 300), new Size(100,30));
DropDownList ddl = new DropDownList("DDL",
new string[] { "This", "is the", "DropDownList", "control" },
new Vector2(300, 100), new Size(150, 30));
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));
RichTextArea rta = new RichTextArea("RTA",
Vector2.Empty, new Size(632, 300), rtext, TextStyle.Default);
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);
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));
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);
hud.EndDesign();
}
public void Render()
{
hud.Render();
}
Points of interest
I tried to mimic the Windows Forms event style and the result looks very similar to it. In the development of my game, it showed to be very helpful because once the needed controls are implemented and fully working, you can concentrate on the game itself, speeding things along. These are the main features of the UI:
- Full windows support: the UI correctly renders multiple windows.
- Modal and draggable windows.
- Shape support: circular, rectangular and trapezoidal default shapes are built-in, but you can define new ones through the
Graphics
static class.
- Border styles: flat, raised, sunken; more can be implemented.
- Control status changes: enabled, highlighted, focused, selected, etc.
- All the mouse and keyboard events you can think of!
The following controls are included:
Window
DialogBox
Label
Panel
Button
TextBox
TrackBar
GroupBox
OptionGroup
CheckBox
TabPanel
Table
DropDownList
RichTextArea
PictureBox
It should be noted that by deriving or improving the PictureBox control you can theoretically develop a whole new set of textured controls, in addition to the renderable ones. You could even combine them to achieve new effects. There are several possibilities. More controls are on the way. To stay updated on the development of this library, refer to the project page at the Star Odyssey website. Please post any bugs you find and requests on the forums.
The Odyssey UI is released under the Creative Commons Attribution NonCommercial license. This source code can be modified as you wish and if you do decide to use it in your project, please contact me telling me about it so we can share links if you'd like. Thanks for reading!
History
- 22 July, 2007: Version 0.3 - Added the Window, DialogBox, RichTextArea and PictureBox controls.
- 14 June, 2007: Version 0.2 - Updated version. Added the Table, Tabpanel and Checkbox controls and the preliminary layering feature.
- 14 August, 2006: Version 0.1 - First CodeProject submission.