Sign in to follow this  
OrangyTang

Designing a game-orientated GUI library

Recommended Posts

I'd like to hear people's thoughts on game-orientated GUI libraries - more specifically I'm thinking of writing my own set of GUI controls for use in my games. Background I tend to write lots of smaller games and experiments rather than single big projects. Recently I've had several on the go at the same time - this works better than expected, when I get tired or stuck with one I can work on another for a while. Also improvements and common code get added which benifit the whole lot. Writing the GUI is always a tedious process, so I'd like to share as much of the code as possible. Ideas Simple, minimal selection of widgets. I'm not doing an RPG here, I just need the basics. So far I'm thinking: - Static text - Static images - Push button - Checkboxes, sliders (for options screens) - Simple text boxes (for high scores) What I don't need: - Multiple floating windows - Tables - Combo boxes - Drop menus - Right clicking - Any sort of automatic layout wizardry - Scroll panes Undecided: - Keyboard navigation - Popup boxes Easily skinable. As I'd like this to be used in lots of different games, I't got to be able to completely change it's look. Probably don't need anything fancy here other than being able to set the textures used for the various controls. Flexible usage. Again, with lots of different games possibly using it I don't want to enforce any kind of rigid usage pattern. Anyone any comments or ideas on this? I think as long as I make it possible to add custom components quite painlessly anything fancy and game specific can easily be added in just for that game (fancy level select screens etc.). And by keeping the actual widgets and the range of interactions simple it shouldn't take me too long to get something workable. Previously I've found that automatic layout managers are one of the most tricky points to get right, so I'm slinging this whole area out on it's arse. [grin] The problem is that even the simplest case is tricky to code with multiple widgets interacting, and even then it's often wrong lots of the time.

Share this post


Link to post
Share on other sites
Writing your own is always a good idea. This way you can customise it for all those little quirks and kwizzles that you want. Take 'Play Online' for instance, that is very customized. Getting a pre-made GUI would allow for a quicker start though, as most GUIs are based on the same principles.

Share this post


Link to post
Share on other sites
Quote:
Original post by rpg_code_master
Getting a pre-made GUI would allow for a quicker start though, as most GUIs are based on the same principles.

Given my platform and libraries of choice there isn't really any existing libraries available which are suitable. There are a couple (one of which I'm already using and hoping to replace) but because they're general-purpose all-singing all-dancing libraries the effort to intergrate and create menus with them makes it far too difficult to just casually drop them in. And the end result ends up being far from satisfactory (ie. it looks like a windows clone, not a game).

I'm mainly after peoples thoughts on how practical (or not) my minimal system will be. I think that 90% of most simple game's GUI can be composed of text + images + push buttons, but I may be missing something obvious...

Share this post


Link to post
Share on other sites
Heya,


I've coded a simple GUI solution on the last place I worked, and I kinda liked it somewhat, at least the main idea.

This was my first attempt at coding a GUI system from scratch. I wanted to manage passing focus between widgets in a simple manner, something like:



// The base class for the whole system
class Widget {

// Stuff

protected:

char Name[64];
Widget *Parent, *Son, *Brother;

public:

// Stuff...

// Also include some nice hierarchy managing methods

// This is good
Widget* FetchNode(const char *name);

virtual ~Widget();
virtual Widget* Input(int code) { return(this); }

};

// And in the main loop, focus manages itself with something like
Widget *Focus;

// Stuff...

// Getting input and translating it to some code, then have
Focus = Focus->Input(input_code);



So what happens is that when you implement your controls in an interface you're creating, just make sure you return what should be the new widget with focus - note the recursive possibilities.

This idea seemed good at start, and it turned out working quite well for some interfaces I ended up writing using this system. I turned up adding support for sliders, radio buttons (which could also act as simple push buttons), text labels, text input boxes and bitmaps.

So what I'm saying is, for what you want, it's quite simple to come up with something good on your own. But if you like my idea, go ahead and knock yourself out.


Cheers.

Share this post


Link to post
Share on other sites
Why no right clicking? It's very handy for context help or simple menus.

I'd also impliment scrolling. Maybe not actual scroll UI element, but the ability for arrow keys or pgup/pgdown to scroll lists of text. Pretty much a necessity for any online game and their chat boxes.

Share this post


Link to post
Share on other sites
I think it's a great idea :), first of all. It is really dull stuff to write, and many games need it.

I would start from a set of interfaces (regular readers of my replies my recognise the pattern here ;D) that define what sort of things widgets can do:
interface IRenderable {
// Some form of communication with a graphics engine
// Could take a Windoze device context if working with OpenGL,
// for example
void Render(RenderingDevice renderer);
}

interface IClickable {
// Delegate responsibility for checking if you clicked it to
// the control. This lets you have non-rectangular controls easily
bool IsInside(int x, int y);
void Click(int x, int y, int button, int clickCount);
}

// must be able to click things to select them
interface ISelectable : IClickable {
bool Selected { get; set; }
}

interface IKeyboardHandler {
void KeyUp(int keyCode, int shiftState);
void KeyDown(int keyCode, int shiftState);
void KeyPress(int keyCode, int shiftState);
}


You can now write a skeleton UI manager that deals with these things:
class UIManager {
ArrayList objects;
ISelectable selected;

void Click(int x, int y, int button, int clickCount){
// called from your input module in some way
foreach(object obj in objects){
if(obj is IClickable){
if((obj as IClickable).IsInside(x, y)){
(obj as Clickable).Click(x, y, button, clickCount);
ISelectable newselection = obj as ISelectable;
if(newselection != selected){
if(selected != null) selected.Selected = false;
if(newselection != null) newselection.Selected = true;
selected = newselection;
}
break;
}
}
}
}

// and similarly for keyboard and rendering
}


You'd probably also want a basic clickable class that covered making a basic rectangular control with a location and size that can draw itself. (I.e., class BaseControl : IClickable, IRenderable {}). Then most of your controls would inherit from that.

Share this post


Link to post
Share on other sites
The only two controls you really need to internally implement in detail are a textbox and image control. Any other type of control can be derived from these using higher level API functions or a scripting language depending how you design the library.

For example a button is just an image control that changes image when clicked on. If you implement image controls to be reactive to mouse clicks, capable of binding to callback functions and also implement an internal animation system whereby you can instruct an image control to change its image after a specified delay then you have a button.

Also its useful to allow controls to contain controls. This means implementing a control heirarchy. This allows prefab controls to be made and then "copy pasted" easily. For example a scroll bar control is actually a transparent image control which contains a child image control for the bar and another for the slider, and a textbox control for the value. All coordinated by top level scroll bar control. In all honestly it would be a nightmare to design any kind of decent looking menu that didn't allow controls to be imbedded in one another.

Another thing that is necessary is a messaging system that allows messages such as mouse clicks to be passed up or down the control heirarchy until they are trapped by a control willing to process it (kind of like windows messaging). Haven't thought about keyboard messages as I have never done them before.

Also it is absolutely critical to make a good control positioning system. You really need the ability to give widths and heights in percentages rather than pixels or else changing resolution will present problems. There is also a ton of hassle with getting text to align and clip to different sized textboxes. If you don't have a simple and solid positioning api then you will end up wasting tons of time repositioning controls using fiddly function calls wrapped in condition statements.

Finally to really speed up development you can build a GUI editor that drives your api. There are so many GUIs in a typical game that its a huge huge waste of time trying to hardcode the positions of controls manually, even if you have got prefab composite controls. I consider GUI design to be a waste of my time personally and have reached the point where I won't do it unless I have an editor. I don't have an editor.


**note case in point - the good-enough GUI system inbedded in doom3 only used images and textboxes if I recall correctly

Share this post


Link to post
Share on other sites
RunningInt: Thats exactly the kind of complexity and over-engineered approach I'd like to avoid. I don't need a seperate scripting layer, or a complicated composition method, or a messaging system. Although you're right in that hardcoding positions/layout is too much like hard work, and I'll be doing a simple positioning tool to do this.

Bob Janova: That sounds like what I'd go for. Minimal amounts of logic in the controls, with plenty of events to hook into so individual games can do what they need to.

Telastyn: Because I don't need right clicking. [grin] When was the last time you had to right click in (say) Doom3? With no need for context menus the right clicking is largely unused.

Scrolling though I'm still not sure on. It's non-trivial to add in a generic and simple to use way, and I'm not convinced that it's totally required. I'll probably leave it out until I find a specific need for it.

Share this post


Link to post
Share on other sites
My GUI is designed for DirectX so its heavilly based on Windows. I started defining a base class like this:


#define KGUI_BASEGUICONTROL 0x0
class KBASEGUICONTROL;

KBASEGUICONTROL* DefaultFunction(KBASEGUICONTROL* me, int kmsg, WPARAM wParam, LPARAM lParam);

class KGRAPHICS3D_API KBASEGUICONTROL{
protected:
BYTE iSubType;
LPK3DRENDERDEVICE lpKRenderDevice;
public:
int ID;

KRECT bounds; //outer limits
LPK3DSPRITEANIMATION background;
float lAccTicks;

KRECT ibounds; //inner limits
LPK3DSPRITEANIMATION image;
float lAccTicksimage;

float margin;

D3DCOLOR backColor;
D3DCOLOR imageColor;

BOOL Enabled;
BOOL Visible;
BOOL MouseIn;
BOOL Selected;
BOOL Pressed;
BOOL wasPressed;
BOOL gotFocus;
int renderMode;
int value;
BOOL Clipped;
KBASEGUICONTROL* next;
KBASEGUICONTROL* parent;
KBASEGUICONTROL* (*lpControlFunction)(KBASEGUICONTROL* me, int msg, WPARAM wParam, LPARAM lParam);

public:
KBASEGUICONTROL(LPK3DRENDERDEVICE lpKRenderer);
KBASEGUICONTROL(LPK3DRENDERDEVICE lpKRenderer, int ID, float PosX, float PosY, float Width, float Height, LPK3DSPRITEANIMATION lpBackground=NULL, int rendermode=KGUI_RENDERMODE_NORMAL, BOOL Visible=TRUE, BOOL Enabled=TRUE);
~KBASEGUICONTROL();
BYTE getSubType();
HRESULT SetKRenderer(LPK3DRENDERDEVICE ilpKRenderer);

HRESULT setBounds(float left, float top, float right, float bottom);
HRESULT setPosition(float x, float y);
HRESULT setWidth(float w);
HRESULT setHeight(float h);
HRESULT setBackground(LPK3DSPRITEANIMATION f);

HRESULT setIBounds(float left, float top, float right, float bottom);
HRESULT setImage(LPK3DSPRITEANIMATION f);

HRESULT setMargin(float m);

HRESULT setBackColor(D3DCOLOR col) { backColor=col; return S_OK; };
HRESULT setImageColor(D3DCOLOR col) { imageColor=col; return S_OK; };
HRESULT setEnabled(BOOL e) { Enabled=e; return S_OK;};
HRESULT setVisible(BOOL v) { Visible=v; return S_OK;};
HRESULT setSelected(BOOL p) { Selected=p; return S_OK; };
HRESULT setRenderMode(int r) { renderMode=r; return S_OK; };
HRESULT setPressed(BOOL p) { Pressed=p; return S_OK; };
HRESULT setClipped(BOOL c) { Clipped=c; return S_OK; };
virtual HRESULT Render(float z);
virtual KBASEGUICONTROL* inBound(float x, float y);
virtual HRESULT animate(float eTime);
virtual KBASEGUICONTROL* getChildbyID(int id) { return NULL; };
virtual KBASEGUICONTROL* processMessage(int kmsg, WPARAM wParam, LPARAM lParam);
HRESULT setControlFunction(KBASEGUICONTROL* (*func)(KBASEGUICONTROL* me, int kmsg, WPARAM wParam, LPARAM lParam));
void getRenderBounds(const LPKRECT lpPoint, const LPKRECT lpRect);
void getRenderIBounds(const LPKRECT lpPoint, const LPKRECT lpRect);
virtual float getWidth();
virtual float getHeight();

HRESULT setValue(int v);
int getValue();

//para evitar el conflicto de pasar punteros de EXE a DLL
void* operator new( size_t tSize );
void operator delete( void* p );

protected:
HRESULT RenderBackground(float z);
};
typedef KBASEGUICONTROL* LPKBASEGUICONTROL;






The DefaultFunction is the basic response function.
K3DRenderDevice is my rendering device and allows for sprite rendering.
K3DSpriteAnimation is an animated sprite class. Having two images allows my objects to have an animated image or background. Thhere is also color information for each one. Having it separated is helpful for many objects like a progressbar but you can live without the background.
Bounds, ibounds and margin allows to move the image separately from the background.
Other values like enabled, visible, etc allows to control status changes.

Notice the control only have a parent and a brother. No childs. Thats because I got another object for that.


class KGRAPHICS3D_API KCONTROLCONTAINER:public KBASEGUICONTROL{
public:
int eCount;
LPKBASEGUICONTROL list;
public:
KCONTROLCONTAINER(LPK3DRENDERDEVICE lpKRenderer);
~KCONTROLCONTAINER();
int addChild(LPKBASEGUICONTROL c);
int addChildFirst(LPKBASEGUICONTROL c);
LPKBASEGUICONTROL getChild(WORD index);
LPKBASEGUICONTROL getChildbyID(int id);
int deleteChildByID(int id);
int deleteChild(LPKBASEGUICONTROL c);
int deleteChildFirst();
int removeChild(LPKBASEGUICONTROL c);
HRESULT FlushAll();
LPKBASEGUICONTROL inBound(float x, float y);
HRESULT Render(float z);
HRESULT animate(float eTime);
LPKBASEGUICONTROL processMessage(int kmsg, WPARAM wParam, LPARAM lParam);
};
typedef KCONTROLCONTAINER* LPKCONTROLCONTAINER;



Its a control but also a control list. The container may hold any number of objects, even other containers. I decided to keep it separated because a container can be used to test all the objects inside while the base control only tests one object. Its just architecture but I like it this way.

With those two controls you may customize anything you want. Like:

class KGRAPHICS3D_API KBUTTON:public KBASEGUICONTROL{
public:
LPK3DSPRITEANIMATION clickImage;
LPK3DSPRITEANIMATION moveImage;
float lAccTicksClickImage;
float lAccTicksMoveImage;
D3DCOLOR disableShade;
D3DCOLOR clickShade;
D3DCOLOR moveShade;

public:
KBUTTON(LPK3DRENDERDEVICE lpKRenderer);
KBUTTON(LPK3DRENDERDEVICE lpKRenderer, int ID, float PosX, float PosY, float Width, float Height, LPK3DSPRITEANIMATION lpBackground=NULL, int rendermode=KGUI_RENDERMODE_NORMAL, BOOL Visible=TRUE, BOOL Enabled=TRUE);
~KBUTTON();
HRESULT setClickImage(LPK3DSPRITEANIMATION f);
HRESULT setMoveImage(LPK3DSPRITEANIMATION f);
HRESULT Render(float z);
HRESULT animate(float eTime);
HRESULT setClickShade(D3DCOLOR col) { clickShade=col; return S_OK; };
HRESULT setMoveShade(D3DCOLOR col) { moveShade=col; return S_OK; };
HRESULT setDisableShade(D3DCOLOR col) { disableShade=col; return S_OK; };
};
typedef KBUTTON* LPKBUTTON;


In this case this button has two additional images for a total of 4: the background, the image, the clicked image and the mousemove image.

One thing missing is a resize feature which I will implement shortly in order to support screen changes.

You may also notice these controls doesn't have a border. And thats right because I have never found the need to use one. But its not that hard to add one.

Hope it helps.

Luck!
Guimo

[Edited by - Run_The_Shadows on March 29, 2006 3:07:16 PM]

Share this post


Link to post
Share on other sites
Quote:

Telastyn: Because I don't need right clicking. When was the last time you had to right click in (say) Doom3? With no need for context menus the right clicking is largely unused.


But when was the last time you had to right click in say... Civ IV? Different games have different needs. If you want to use it in 'lots of different games' the need will come up. Given the triviality of including it, it just seems a little silly not to, imo.

Share this post


Link to post
Share on other sites
Quote:
Writing the GUI is always a tedious process, so I'd like to share as much of the code as possible.


Yes it is... I have written 2 GUI libs in past, one of them being especially complicated, and found it really tedious and boring at times. However, the ability to develop UI for games with very little work absolutely recompensed my efforts.

OrangyTang, have you thought about writing this kind of library not only for you, but also for other people? I ask, since I am really interested in writing one open-source / portable / C++ / flexible / rather simple / GUI library for game programmers, and it would be good to work together.

You may say that there are literally thousends of other GUI libraries out there on the internet, so why won't you join them or start your own? The facts are, your design is very similar to what I thought about how my next GUI lib should look like. So, are you interested in creating such "general purpose" (but not too general, ie. kitchen sink not included) GUI library with me (at least, since Deffer was also interested in writing one with me). I applied for Google Summer of Code 2005 with my "GUI library targeted at game programming", but was rejected (probably due to the fact, that Blender group didn't want such useless thing coded for them ;-) ) and from that time, had no time to start working on it on my own (uni, other projects etc. take time), but I am still very interested in writing one.


Anyway, my comments about your lib:

+ add: XML used for storing widget properties
+ add: WYSIWYG layout editor which allows to drag n' drop controls, edit properties etc.; I found that having even very simple layout editor makes implementing UI much easier task; what's more, it could be implemented using this GUI
+ it's good you don't want to implement automatic layout packers - I found them rather useless for games, since usually you DO want to have absolute control of your widgets
+ things to strive for: flexibility, RAD, ease of use (for developer), portability
+ flexibility: easy way to add new controls (ie. specific to game), ability to subclass other ones
+ abstract interface for every used external library, so that they can be easily changed to others (ie. SDL render things too slow for you, so you write OpenGL plugin)
+ skins
+ add: resolution independent control positioning / scaling!
- missing widgets: floating windows, drop-down lists, key binders
- I second Telastyn - adding the right-click ability is next to trivial, but it does have some very interesting effects
? you didn't write anything about communication GUI <-> programmer... callbacks, generic functors, sockets and signals or what?

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
According to your feature list you're planning to rewrite cegui (www.cegui.org)

Share this post


Link to post
Share on other sites
This is what I've done with the GUI system in my own game. It works great (draggable dialogs are a bit wonky but otherwise...). I highly recommend going this route in terms of configuration... it might require a bit more work initially but the results are highly worth it :)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Desktop -->
<desktop>

<!-- Dialog -->
<window type="dialog">

<caption>Quit?</caption>

<x>256</x>
<y>128</y>
<width>256</width>
<height>128</height>
<color red="255" green="255" blue="" alpha="255" />
<visible>1</visible>
<mousedetect>1</mousedetect>
<movable>1</movable>
<background>91</background>

<!-- Static Text -->
<window type="statictext">
<caption>Are you sure you wish to quit?</caption>
<x>0</x>
<y>40</y>
<width>256</width>
<height>16</height>
<color red="255" green="255" blue="" alpha="255" />
<visible>1</visible>
<mousedetect>1</mousedetect>
<font>tahoma</font>
<justify>1</justify>
<textwidth>256</textwidth>
</window>

<window type="button">

<caption>Yes</caption>

<x>55</x>
<y>67</y>
<width>64</width>
<height>32</height>

<color red="255" green="255" blue="" alpha="255" />

<visible>1</visible>
<mousedetect>1</mousedetect>
<tile idle="94" mouseover="94" click="95" attention="94" />
<font>tahoma</font>

</window>

<window type="button">

<caption>No</caption>

<x>135</x>
<y>67</y>
<width>64</width>
<height>32</height>

<color red="255" green="255" blue="" alpha="255" />

<visible>1</visible>
<mousedetect>1</mousedetect>
<tile idle="94" mouseover="94" click="95" attention="94" />
<font>tahoma</font>

</window>

</window>

</desktop>



Share this post


Link to post
Share on other sites
Quote:
Original post by playmesumch00ns
www.cegui.org.uk


Crazy Eddie's GUI System is pretty nice, but it's a little too much in my opinion, especially considering the OP's needs. It also seems like there will never be a final release for it.

I haven't had much luck with the library myself. It's too bulky for my needs (its inclusion of other libraries, its attempt at supporting every type of control, etc.), and when I tried integrating it into one of my projects it would cause the application to crash when exiting. So after a few days of moving things around to get it to fit sungly in my library, I just ripped it right out [lol].

I'm writing a library to use in making simple 2D games, which includes a GUI along the same lines as the one you describe, but at this rate, I wouldn't count on it ever being finished [lol].

Writing a GUI is a little tedious at times, but overall I think it's pretty fun. It's one of the few systems that lends itself so easily to OOP, that once you've got the base classes in shape, the rest just rolls right along.

As a side note, it's really easy to try and support too much once you start writing a GUI. If you decide to write one yourself, I advise you to keep it as simple as possible, despite all tempatations.

Share this post


Link to post
Share on other sites
Bob Janova: maybe you could write a small tutorial. I think there is a lot of interest in this subject and a lot of people would benefit from it.
Your explanation is ok but it would still take some time to figure out the details.
I know there are some resources already there like the article Mason McCuskey wrote or Chad's tutorials. However both of them are far from being simple. It takes a while to go over and pick up the good parts.

[Edited by - Calin on April 7, 2006 3:15:37 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by rick_appleton
How's this going then?

Slowly. [sad]

In an effort to improve code sharing between a few projects I've been refactoring existing code (like mouse event generation) and extracting it into a shared library. While this is no doubt useful work it does mean lots of time consuming code changes as I upgrade the existing code to work with the new interface.

Since I'm going to get to some of the 'interesting' parts soon I'm going to start posting progress on my journal. It might not get read but it helps keep me motivated. [grin]

Share this post


Link to post
Share on other sites
Calin: I'd love to, but I haven't actually written such a library myself and I haven't really thought about it much beyond the framework I posted here :D. Having said that, I did write a similar system for objects on a geological map, so maybe I could write something up. The implementation details depend hugely on the rendering API one is designing for, though.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this