Game GUI design...

Started by
4 comments, last by sirGustav 18 years, 10 months ago
I was just wondering how people here have implemented such a feature - do you mostly follow the WinMFC lines of a CWindow type class, which everything derives from? All those different messages seem un-needed, but how do you implement OnClick()? I'm considering making my game menus etc a keyboard-only affair, so I'll need a very strict enforcement that you can actually navigate properly around the various controls. does your GUI or the same or just use the mouse? Oh and I figured .xml files make a nice dialog/menu script?
Advertisement
To get to the low-level of my meny I have a 3 level design with state-mashnes:

Level 1: Currently 3 nodes(meny, console and game). The console dispatch key-events to the console, the game key and mouse events to the game, and the meny to the (you guessed it) to the meny.

Level 2: Basicly a bunch of intro nodes.
1: startup node, waits about 1/2 seconds
2: plays the game name on entry and shows title, and "press any key to continue"
3: fade title to meny
4: display meny and dispach any key and mose events to level 3

Level 3:
Basicly a really large fsm where each node is represented by a structure looking rougly like this(Meny):
typedef struct _entry {  char text[80]; // the text to display  int type; // button or other.  int highlighted; // 1 if highlighted, false if not  int x; // x pos  int y; // y pos  int used; // 1 or 0  VOID_FUNCTION onclick; // what happens when we click on this} Entry;typedef struct _page {  Entry entries[10]; // maximum of 10 entries  Entry* edit; // the edit (if any)  int previousPage; // used when the player presses esc.} Page;typedef struct _meny {  Page pages[NUMBER_OF_PAGES];  int currentPage;} Meny;Meny g_meny;

( it's ugly - I know :) )
Basicly, it boils down to this: The meny contains a number of pages, such as new game, options or whatever. Each page has an entry. An entry ´can be a button, a label, a check-box whatever.
When I click on something the event is sent down to the meny(in reality I call clickMeny(x,y) ). The meny then sends the event to the current page, wich is basicly a for-loop(of 10 in the code above) finding the current entity(point in box or not). If I find something in the loop, I call the onClick funtion and return.
The onClick may change the currentPage in the meny-struct(since it is a global), start a new game or set the global running to false(0 in my case).

The same is about keyboard events, but they stop the page instead(since it knows which the current edit is).

A short note about type, and highligted members of entry:
if type==button then the highlihted member is updated automatic(representing if the mouse is over the entry or not).
if type==other updating highlighted is done in onclick().
for labels it isn't updated, for checkboxes it represents the state of the box(on or off), for radios it will disable all the others and enable the current, etc.

long answer I know, but you wanted to know about my system :)
For games, I prefer to abstract between user input and game logic. This makes it much simpler to implement things like control mapping, for instance. Basically, user input is funneled down into a mapping/translation system that turns the input into internal "action" codes and information (eg. "MOUSE_CLICK" at "483,224") This is then passed to a master object that dispatches the input to a number of listener clients. Clients voluntarily request to be notified of input or removed from the listener list at run-time. Each client registers a set of callbacks that receives the input action and any extra details like mouse coordinates. Usually I set this up by defining a CUIListener interface and having individual client objects implement this interface.

I then set up a GUI layer implemented separately that registers interest in user input events. The callbacks are informed when events of interest occur; a simple bitmask is used to flag which events the listener cares about. The individual UI clients then decide if they should act on the input, and then act on it as needed.


This is a bit excessive for simple client apps, though, for which I'll generally favor a vaguely MFC-like structure, if I'm not already using MFC or a language better suited to rapid high-level app development.


XML can be a very powerful tool for defining interfaces, but KISS - there seems to be a buzzword trend lately to use XML for absolutely everything, which is unhealthy. If your interface isn't complex or dynamic enough to need a full XML framework for defining it, then don't use XML. That said, check out Trillian's skinning engine for a great example of XML-based interfaces in action.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]


Quote:
I was just wondering how people here have implemented such a feature - do you mostly follow the WinMFC lines of a CWindow type class, which everything derives from? All those different messages seem un-needed, but how do you implement OnClick()?


I'm not following it, since for me, MFC is huge, bloated and evil thing - I've developed my own GUI library around concepts that this series of articles pointed to me: clicky!

Just read them all, and you'll know what I mean :-)

Quote:
Oh and I figured .xml files make a nice dialog/menu script?


Yes, but they tend to grow very fast, so having WYSIWYG layout designer is very handy. Ie. in my last game (Herbata kto ty tak kroliczek), .xml file with menu definitions grew to ~40 kb - editing it by hand was pain in the butt :-/

<OT>

sirGustav: a little improvement to your system, would be to use STL vectors, so that there won't be this limitation to 10 entries per page anymore. Also, using std::strings bring more safety, and reduces memory consumption. Sth like this:

typedef struct _entry {  std :: string text; // the text to display    int type; // button or other.  int highlighted; // 1 if highlighted, false if not  int x; // x pos  int y; // y pos  int used; // 1 or 0  VOID_FUNCTION onclick; // what happens when we click on this} Entry;typedef struct _page {  std :: vector < Entry > entries; // maximum of infinity entries :-)  Entry* edit; // the edit (if any)  int previousPage; // used when the player presses esc.} Page;typedef struct _meny {  std :: vector < Page > pages;  int currentPage;} Meny;Meny g_meny;


</OT>
I'm unfamiliar with MFC, so this might be quite similar.

In my previous project, everything rendered inherited from a base class. The base class arranged itself into a tree. The base class held simple overridable functions to manipulate the object [move, moveto, rotate, rotateto, resize...] and to gather data about it [getrect]. It also held optional functors for keyboard entry and/or mouse clicks.

Since the way the tree was formed determined the drawing order, it was easy to determine which object was the "highest" which had the input [keypress or mouse click] handler. The functor would then be directly executed.

I'm not sure if I'm going to use this again though. It made for fairly easy reuse, and it made for flexible creation of a single object that had no interaction with another. When the design required interaction between renderables though, it became a little tedious and unwieldy.
Quote:Original post by Koshmaar
<OT>
sirGustav: a little improvement to your system, would be to use STL vectors...
</OT>

<OT>IIRC templates and std::strings are hard to use in C(hence the use of structs and typedef) :)
and I'm not ready to "throw away"/"convert to c++" 2-1/2-years game-code (I'd rather finish it(1/2 year) and start clean)</OT>

To whom it may concern:
If you plan to use a code like my system above, possible improvements(other than Koshmaar's):
ditch the function-pointer-macro stuff and make it a virtual function/functor(see the enginuity-article by superpig(I hope I got the name right) here on gd.net for ideas) instead.
Remove the "used" member(since it is no longer needed when using vectors) and remove some ints and replace them with bools(for clarity) instead.
There are some more stuff that I can think of to fix, but in short: don't copy the code - learn, understand and implement instead ;)

This topic is closed to new replies.

Advertisement