Archived

This topic is now archived and is closed to further replies.

Shambles

Designing a GUI...

Recommended Posts

Shambles    122
Pre script: This is kind of a game design/game programming hybrid post. Hi. I''m currently designing/programming a GUI which will be used not in-game; but as a menu which comes before you play, and perhaps after (to show scores or what-not.) I''ve spent a few weeks planning it out and peeking at other game source''s GUI. I''ve come up with a CMenu class which contains instances of a struct called ''menu''; and methods which initiates the struct, and adds to the struct. CMenu also has a pointer to the currently active menu struct (*activemenu). The most recent addition I made to CMenu was the Main() function which is called from the main loop. I had to add states to it which include ''MENU_OPENING'', ''MENU_MAIN'', ''MENU_TRANSITION'', and ''MENU_CLOSING''. Here is where my problem starts. I just noticed that I can only have one type of "transition" between menus. Is this good design practice for a GUI? Also, I''ve noticed that some games, (Warcraft 3 and Dungeon Siege,) use a constant background for many of the menus. Is it possible to implements such a type of menu with a "every-menu-has-a-structure" approach? Thanks in advance, -Shambles P.S. Perhaps this is more of a game programing question, but the end-user design of my menu is my real concern.

Share this post


Link to post
Share on other sites
Red Ghost    368
I do not understand what is the effect of the state MENU_TRANSITION. Can you explain ?

This is how I designed my simple GUI:
A menu class is not very much different than a dialog box class:
- a dialog box has a header bar with a title and a display rectangle where all controls are drawn. its location may be anywhere on the screen.
- a menu has just a display rectangle anywhere on the screen (either horizontal or vertical) where controls are drawn.
Thus my menu can be represented by text controls and/or by picture controls. When a text or picture control is clicked a message is sent to the game engine to do the resulting action (either open a menu/dialog box, or do an action).
Thus a dialog box is general implementation (GUIBox) and Menu is a special implementation (Menu derived from GUIBox) since the only difference is the header bar.
The Display object holds the base display information like the font size and spacing, the GUI colors and sprites. This object also is a factory that builds the GUIBox from a tokenized resource file. This is my way to solve the common GUI features: everything that makes a GUI unique is in the factory. Everything that is menu specific or control specific is in its respective class.

The classes are as follow:

  
//Base class for all Gfx objects it holds coordinates and size.

class GfxObject
{
public:
int x, y;
int width, height;
GfxObject(void): x(0), y(0), width(0), height(0) {};
};

//Base GUIBox class

class GUIBox: public GfxObject
{
protected:
//description of the dialog box

String Caption;
//format of the dialog box (background sprites, colors, font type and size , ...)

Display* DialogDisplay;
//Message manager from the engine

Message_Manager* MSG_Queue;
//mouse position in the dialog box

int mouse_x, mouse_y;
//last mouse position for box drag

int last_mouse_x, last_mouse_y;

//Control list

Control* f_control; //first control of the list

Control* c_control; //current control (used to traverse the control list)

Control* focus_control; //focused control


public:
GUIBox(void); //base constructor

GUIBox(String MyCaption, int BoxWidth, int BoxHeight); //constructor initiating box in middle of screen

GUIBox(String MyCaption, int xPos, int yPos, int BoxWidth, int BoxHeight); //initiates box at pos x,y

virtual ~GUIBox(void);
virtual void SetDisplay(Display* MyDisplay); //gets a pointer to display specs (font type and size, colors, etc...)

virtual void SetQueue(Message_Manager* MyMSG_Queue); //gets a pointer to the engine msg queue

void SetPos(int xPos, int yPos); //sets box position on screen and checks the box is displayed fully on screen

virtual void Update(MemoryBuffer* Buffer); //draws box to the screen buffer

bool CheckClick(int xPos, int yPos); //checks wether the box has been clicked

virtual void Proc(int Msg_type); //processes any event happening in the box

void AddControl(Control* NewControl); //adds a control to the control list

void DoFocus(void); //if an event occured within the box, the focused control is processed first

void AdvanceFocus(void); //if a change focus event occured, the focus control is set accordingly

};

//Menu Class

class Menu: public GUIBox
{
public:
Menu(void): GUIBox() {};
Menu(String MyCaption, int BoxWidth, int BoxHeight);
Menu(String MyCaption, int xPos, int yPos, int BoxWidth, int BoxHeight);
virtual ~Menu(void);
virtual void SetDisplay(Display* MyDisplay);
virtual void SetQueue(Message_Manager* MyMSG_Queue);
virtual void Update(MemoryBuffer* Buffer);
virtual void Proc(int Msg_type);
};

//Base Control class

class Control: public GfxObject
{
friend GUIBox;
friend Menu;

protected:
Display* ControlDisplay;
Message_Manager* MSG_Queue;
Control* n_control;
String Caption;
int CaptionSize;
bool Focus; //checks wether the control has the focus or not


public:
//base constructor

Control(void): ControlDisplay(NULL), MSG_Queue(NULL), n_control(NULL), Caption(""), CaptionSize(0),
Focus(false) {};
//full initiation constructor

Control(int xPos, int yPos, int ControlWidth, int ControlHeight, String Name) {};/**/
virtual ~Control(void) {};
//draws the control to the screen buffer

virtual void Update(MemoryBuffer* Buffer,int xPos,int yPos) {};
//checks wether the control has been clicked

bool CheckClick(int xPos, int yPos);
//process a control event

virtual void Proc(int Msg_type) {};
};

//Derived Clicked Control class (example: an OK button)

class ClickedControl: public Control
{
friend GUIBox;
friend Menu;

protected:
//state of the control (activated or not)

bool State;
//ID of the beneficiary of the message (it can be the game engine for a quit message or a game agent or ...)

char Msg_Dest;
//type of event to process by Msg_Dest

int Msg_Type;
void SetState(bool NewState);
bool GetState(void);

public:
//initiation constructor

ClickedControl(int xPos, int yPos, int ControlWidth, int ControlHeight, String Name, bool Init_State,
char Dest, int Msg);
virtual ~ClickedControl(void);
//draws the control to the screen

virtual void Update(MemoryBuffer* Buffer, int xPos, int yPos);
//process the event - if the control is activated, the Msg is sent to the message queue

virtual void Proc(int Msg_type);
};



As a side note I do not use much the STL library by deliberate choice:
- the STL library is not up to date (it was with my Borland C++ 5.01 and I haven''t changed since - BC++ 5.01 was before Borland C++ Builder). Hence the String class you can find.
- the controls are objects which do not have the same size (I have text controls, data controls displaying agent states, picture controls, etc ...). A GUIBox may have many different controls.

Hope that helps.
Ghostly yours,
Red.

Share this post


Link to post
Share on other sites
Shambles    122
My GUI is not a typical GUI (it does not support windows.) I have a class called CMenu. CMenu is actually a manager class for the menus that will be shown before the actual game (such as load, credits, options...) Inside CMenu there is a pointer to the active menu (menu *activemenu. What I could do is just make a button in the active menu change the activemenu pointer to point to a new menu but that would be not natural and is not what I''m aiming for. So I added a transition state to switch between menus:
  		case MENU_TRANSITION:
if (!transmenu)
state = MENU_MAIN;

DrawMenu(transmenu, 1.0f);
DrawMenu(activemenu, alpha);

if (alpha <= 0.0f) {
activemenu = transmenu;
state = MENU_MAIN;
} else
alpha -= 0.02f;

break;
This is done by CMenu.Main() which is the function called if the state of the program is set to STATE_MENU (instead of STATE_GAME). But the limitations of this allows for only one type of transition between menus. Perhaps I should go more OOP...

Share this post


Link to post
Share on other sites
Kylotan    10004
quote:
Original post by Shambles
I had to add states to it which include ''MENU_OPENING'', ''MENU_MAIN'', ''MENU_TRANSITION'', and ''MENU_CLOSING''. Here is where my problem starts.

I just noticed that I can only have one type of "transition" between menus. Is this good design practice for a GUI?

What do all these states mean, in human interface terms? I mean, my menus don''t have an opening or closing state. They''re just either open or closed (assuming the menus like those in MS Windows). If you mean the actual act of growing and shrinking... well, I turn that off because it''s a pointless waste of CPU time. The presence or absence of the menu is ample feedback to the player on whether they clicked in the right place or not. Animation is eye candy in one of the places where you really don''t need it, as it''s getting in the way of the primary task which is to get and send information. All interfaces should be designed with the primary task in mind.

I don''t know what the ''transition'' means either. I know the dictionary definition, but I''ve no idea what it means for your menus.

quote:
Also, I''ve noticed that some games, (Warcraft 3 and Dungeon Siege,) use a constant background for many of the menus. Is it possible to implements such a type of menu with a "every-menu-has-a-structure" approach?

Again, this isn''t very clear... a menu has standard behaviour such as being able to appear, disappear, show you the options (possibly with graphics), and execute commands. The actual text, commands, and appearance of a menu is just data rather than something you hard-code in. You can create one CMenu and set its background to abc.bmp and create another CMenu and set the background to xyz.bmp. You reuse exactly the same code in each case.

[ MSVC Fixes | STL | SDL | Game AI | Sockets | C++ Faq Lite | Boost | Asking Questions | Organising code files | My stuff ]

Share this post


Link to post
Share on other sites
Shambles    122
All replies to my GUI related posts indicate to me I''m going about this the wrong way. I started basicly from scratch execpt for a little influence from quake3 C gui code... (notice *activemenu) In my model, there is only one visible menu, and that is the one pointed to by the *activemenu pointer.

Eye-candy for my GUI is a priority, it cannot be compromised. Although, the animations and such will be quick enough (through tests) to not bother the player. Menus which change instantly arn''t very professional in my view.

Share this post


Link to post
Share on other sites
pan narrans    1284
quote:
Original post by Shambles
Menus which change instantly arn''t very professional in my view.

But what they are is quick and easy to navigate. The most annoying thing about DVDs is the animations and transitions that occur when you click menu buttons.
Have you ever been to one of those Internet sites that slowly fade-in to your browser? Interesting the first time, goddamn annoying every time after.
It is very important to consider functionality of highest importance when designing a GUI.


pan narrans
Study + Hard Work + Loud Profanity = Good Code

Share this post


Link to post
Share on other sites
Shambles    122
quote:
Original post by pan narrans
Have you ever been to one of those Internet sites that slowly fade-in to your browser?

Yes and I just hate it. But what i''m talking about goes fast enough to not annoy the user.

quote:
Original post by pan narrans
It is very important to consider functionality of highest importance when designing a GUI.

I can''t imagine a player clicking lightning speed through menus, but if there were; It would only really benefit them and cause a eye-sore for the rest of the users. Smooth transitions which are easing yet quick are ideal (as apposed to assaulting the user''s eyes like those brain-washing machines they have on tv!.)

Share this post


Link to post
Share on other sites
Oluseyi    2108
Design your menus so they can be fully manipulated using only a 4-button joystick/gamepad. Any menu that requires me to drop my gamepad(s) and pick up my mouse breaks the continuity of the experience, and points to overall structural flaws.

Don''t bother about menu transitions (opening, closing, fading, whatever-ing). Instead, highlight the current menu item (what the user is pointing at or will activate by clicking/pressing the ''execute'' button). If your menu has items that pop up, leave the space there (but leave it empty for non-popup items) and fill it with the popup when necessary. Minimize distractions, because the menu''s purpose is to get me (back to) playing as quickly as possible.

Abiding by these principles, it becomes clear that menus are merely collections of menu items (which may be specific types of sub-menus as well), and that menu items are graphical widgets that can be selected or not and pressed to initiate an action or indicate a preference.

Finally, you may want to make menu structure independent of presentation. You definitely want it to be independent of background image. NBA Live 2001 from EA Sports had a background that only animated when you selected different major submenus (rosters, controller setup, audio/video configuration, etc); it rotated or otherwise transformed the current camera view to make a different area of a basketball court schematic the background. Simple. Effective.

Share this post


Link to post
Share on other sites
Red Ghost    368
Let me answer more specifically to the transition state:
So CMenu is your Menu factory. I understand that it is used before or after the game but not during: so speed of display of information is not a concern. It could be simpler by adding Open and Close animation to your menu object directly. You also could use a strategy pattern with different opening and closing menu so that you can allow the user to switch on or off your menu transitions. Your architecture could become something like:


  
struct Coord //used to store coordinated for animation purpose

x, y
width, height
alpha
//

//

Strategy //Base strategy class that you derive into OStrategy for opening and CStrategy for closing

public
//besides the constructor

virtual void DrawAnimation(Coord* End) //the menu will give a pointer to its MenuData (see below)

//

//

Menu //Base Menu class from which all menus are derived

protected:
Coord MenuData //basic data like width, height, pos and menu entries or text control list

OStrategy* Opening
CStrategy* Closing
bool Open (void) //will display the opening animation

bool Close (void)//will display the closing animation

public:
Menu(..data.., Ostrategy* NewOpen, OStrategy* NewClose) //calls protected method Open

~Menu(void) //calls protected method Close before deleting

//other methods

//

//

CMenu //Menu Manager

private:
OStrategy GeneralOpening //either you put here a consistent opening and closing animation

CStrategy GeneralClosing //or you create a private method that allocated a random animation strategy

protected:
Menu* ActiveMenu //current menu displayed

public:
//besides Manager class constructor or destructor

bool OpenMenu(MenuID)


The menu object is then responsible for checking wether there is an animation strategy or not. If no strategy, the menu is displayed directly, else the strategies are played.
Then opening and closing a menu will be as simple as:


  
bool OpenMenu(MenuID)
//checks wether a menu is displayed or not

if ActiveMenu != NULL
delete ActiveMenu //will automatically play a closing animation

//creates a new menu

switch case MenuID
case CONFIG //an example of menu: configuring a player input device

ActiveMenu = new MenuConfig(..Initiating data.., GeneralOpening, GeneralClosing)
//will automatically play an opening animation

case ... //and so on

//returns the result of creation

if ActiveMenu != NULL
return true
else
return false


Hope that helps.
Ghostly yours,
Red.

Share this post


Link to post
Share on other sites
Shambles    122
Thanks for the replies. I''ve just redone my whole code and I''m happy with it now.

Red Ghost: your code really helped, thanks!

Oluseyi: I also just added support for more than the keyboard and mouse (I don''t know what I was thikning only including mouse/keyboard...)
quote:
Minimize distractions, because the menu''s purpose is to get me (back to) playing as quickly as possible.
I''ve noticed that in some games (Half-Life, Dungeon Siege) the menu transition never bothered me nor took too long.

Share this post


Link to post
Share on other sites
Oluseyi    2108
quote:
Original post by Shambles
Oluseyi: I also just added support for more than the keyboard and mouse (I don''t know what I was thikning only including mouse/keyboard...)

Excellent!

quote:

I''ve noticed that in some games (Half-Life, Dungeon Siege) the menu transition never bothered me nor took too long.

Well, I remember NBA Live 98 had this elaborate line-drawing routine for every screen. It got old fast. I don''t buy games to look at your delicately crafted menus; I buy them to play. The purpose of a menu is to allow me configure, suspend or restore my playing experience. Make it so.

Share this post


Link to post
Share on other sites
Shambles    122
Maybe your right; but I should at least design a menu system which is able to incorporate transitions, even if it will never be used. But perhaps I can do it at a generic level: derive from the menu class, and add opening() closing() transition(menu *tranit_to) which should all be called from the event() function wich is virtual...

Share this post


Link to post
Share on other sites
Kylotan    10004
The 400ms wait times in Windows menus make me mad. Anything over 200ms is just gonna piss me off. As mentioned above, the purpose of the menu is really to let me change some things and get me back into the game. Animation and eye-candy is all very well as long as there''s no ''dead'' spots where I am waiting for it to do something.

[ MSVC Fixes | STL | SDL | Game AI | Sockets | C++ Faq Lite | Boost | Asking Questions | Organising code files | My stuff ]

Share this post


Link to post
Share on other sites
Shambles    122
I see. So if I ever wanted to make a >200ms transition, It would have to actually serve a purpose? Such as: prupossly comment out the code which pre-loads all menus which will purpossly make you wait for every menu to wait therefore allowing neat loading-menu transitions? Just toying with you.

Quake3 switched between menus instantly but it always had a pause between transition where I heard the currently cached sound play again like a broken record until the menu I'm changing to finishes to load. Far worse then anything I could image a commercial game to be. BTW I run on a PII 300Mhz (so old!) with 256MB of SDRAM and a 7200rpm hard drive (which makes me load half-life maps faster than those pitifull 5400rpm'ers.)

[edited by - Shambles on October 3, 2002 10:30:28 PM]

Share this post


Link to post
Share on other sites