Skip to main content
GameDev.net gamedev.net
🔒 Locked

Building a menu system

Started by gamedev90 Aug 31, 2009 at 1:16 AM 44 replies 12.3k views
Original Post
gamedev90
gamedev90
Hello, I am building a menu system using opengl api(not winapi menus) and different menus stay static on the screen.Can some one help me by pointing to some tutorials or some papers please.This is menu system something like we see in rts games.
Scourage
Scourage
Checkout IMGUI techniques. I think they may be a good fit for what you are trying to do. Watch the video, it's pretty good.

https://mollyrocket.com/forums/viewtopic.php?t=134

cheers,

Bob

[size="3"]Halfway down the trail to Hell...
gamedev90
gamedev90
Thank you very much for the reply scourge.I also have heard about event based systems.Are both Immediate mode and event based systems the same??
Trienco
Trienco
Only 10min into the vid, but so far it sounds like "get rid of all the elaborate stuff that would create clean and structured code and replace it with a huge-ass spaghetti-code if-else-orgy that only a C-hacking mother could love".

To answer the other question: no, "immediate mode gui" seems to be the very opposite of event based, because so far it sounds like one huge function asking every single interface widget if it was clicked. But I may (hopefully) be wrong there.
f@dzhttp://festini.device-zero.de
Yann L
Yann L
Quote:
Original post by Trienco
Only 10min into the vid, but so far it sounds like "get rid of all the elaborate stuff that would create clean and structured code and replace it with a huge-ass spaghetti-code if-else-orgy that only a C-hacking mother could love".

That's basically the idea that guy is advocating. While I skipped a lot of the video, it does seem like he did not actually understand the concept behind OO event driven UIs at all.

Quote:
Original post by Trienco
To answer the other question: no, "immediate mode gui" seems to be the very opposite of event based, because so far it sounds like one huge function asking every single interface widget if it was clicked. But I may (hopefully) be wrong there.

No, you're right. It's polling every single UI element in a huge loop, while every single event handler of the entire UI is entangled somewhere deep in one huge if/else tree. In other words, IMGUI is a really, really bad idea.
gamedev90
gamedev90
Hi You finally say that IMGUI is bad???Can some one provide tutorials or materials to write a event based menu system please.Are there any other menu systems better than event based menu systems??
CadetUmfer
CadetUmfer
Quote:
Original post by Yann L
It's polling every single UI element in a huge loop, while every single event handler of the entire UI is entangled somewhere deep in one huge if/else tree. In other words, IMGUI is a really, really bad idea.


In traditional Retained Mode GUIs, you create controls, query controls' state/data, listen for controls' events, and destroy controls. All of this code is in different places.

IMGUI says the application should own the data, and only the caller needs to know about the events. This results in all of the code being in one place, and a complete separation between behavior and visual representation.

You end up with UI code looking like this:
switch (UI::Button(UI_ID, Rect(40, 110, 88, 80))) {    default:        // draw the button's normal state        break;    case UI::State::HOVER:        // draw the button's hover state        break;    case UI::State::CLICK:        // draw the button's clicked state        // act on click event        break;}


You don't need to manage controls' lifetimes, or query/copy their data because they own no data. You don't need to wire up events, because they return them immediately.

It's the leanest, fastest, easiest to understand UI code I've ever worked with. I'll never go back to CEGUI, that's for sure.

EDIT: And it's not as though you need to have your data defined in code as above. You could easily write an XML representation, and parse those elements into IMGUI calls with no change in the underlying UI code.
Anthony Umfer
Trienco
Trienco
Quote:
Original post by CadetUmfer
In traditional Retained Mode GUIs, you create controls, query controls' state/data, listen for controls' events, and destroy controls. All of this code is in different places.


If you feel a constant need to query state/data of controls and handling their events is an issue, as well as you make it a big deal to create and destroy them, my guess is that you're doing something in a very weird and basically wrong way.

//somewhere in your appvoid Game::buildSoldiers() {...}//whereever you setup your menuButton* btn = new Button(10,10, 150, 20, menu);btn->onClick = bind(&Game::buildSoldiers, gameInstance);btn->setImage(xxx);


What else would you want to do for a button? Why would you need to query its state? Why would you care about its internal data? Where do you need event handling, if you go for the callback solution? Why would you worry about destroying your controls, when every half decent gui I ever used will automatically destroy all child objects, meaning all you do is: delete menu;

Quote:
This results in all of the code being in one place,


Which can be a good thing, if a) the code has any business all being in one place and b) "all in one place" doesn't equal "one huge if/else switch/case monster function doing everything".
f@dzhttp://festini.device-zero.de
gamedev90
gamedev90
Is event based menu system a kind of retain mode GUI???can someone help me in
how to start writing them.
Fingers_
Fingers_
This is an interesting topic, as I'm in progress of developing a new GUI system as well. My old GUI code works just like Trienco's example, but I keep wondering if it would be better from an encapsulation point of view to use an event system rather than a callback.

Basically, rather than setting btn->onclick to a function pointer you would set it to an enum value. When GUI registers a click on btn, it adds an event with that value into a queue. Whenever you decide to evaluate user input, you go through the GUI event queue and if there's a button click event with the value "buildsoldiers" you call the relevant code.

What are your thoughts? Is it just a question of aesthetics? I will probably need some callbacks anyway in order to allow more advanced customization of GUI objects (e.g. completely replacing how a button is rendered) but I see this as being a different level of user involvement.

(regarding IMGUI, this indeed sounds like a horrible waste if it works as you say)
Scourage
Scourage
My experience with IMGUI has actually been very good. There is a simple api that is called during the render sequence that handles user input, hit action and rendering all at once. Behind that simple API can be quite a large system of buffering draw calls, buffering objects, handling pop-ups, etc. My implementation is object oriented and does deferred rendering to handle overlapping windows properly.

To tie this into an event based system (which mine is) you simple need to feed the events to the UI context so that it has the current user input state. All the widget code will check the UI context when they run to check for hit tests and button clicks. This still occurs with event based widget libraries except the event is pushed to the widget, not the widget coming to the event.

The point an IMGUI is to reduce the complexity of using the API that the developer uses to create a UI in a real-time system. Having built both an IMGUI and a "retained mode" gui libraries from the ground up in production environments, I can say that they are both complex pieces of software, but using an IMGUI api is an order of magnitude simpler than a retained mode gui api, especially for dynamic UIs.

For an open source example of a complex IMGUI, look at Blender. It's an IMGUI and has quite a bit of functionality, especially the 2.5 version.

For what it's worth, the author of the video does not like OO code. I'm certain he understands OO design and it's capabilities, it's just not what he prefers. He's a fairly successful game developer and may be on to something even though I don't necessarily agree with him.

My 2 cents,

Bob

[size="3"]Halfway down the trail to Hell...
CadetUmfer
CadetUmfer
Quote:
regarding IMGUI, this indeed sounds like a horrible waste if it works as you say

A horrible waste of what? It takes much less time/code to write a simple IMGUI for a game compared to a full-featured RMGUI. If you're using a good GUI library then rock on, but if you're writing one from scratch you'll get way more done way faster by just letting your app have control.

Why should a control care how it's drawn?
Why should a button care what happens when it's clicked?
Why should a textbox care what text is in it?

These are things that the application is concerned with, not the UI library. Both are valid ways of doing the same things, but saying IMGUI=spaghetti code is plain wring. IMGUI gives the application control over the control flow. So you can write crap, but you shouldn't. My UI code all looks like what I posted above--pass in the data, handle the events. The fact that it has zero overhead (no allocations, not even a function pointer) is just a bonus.

EDIT: Even NVIDIA has caught on
Anthony Umfer
Trienco
Trienco
Quote:
Original post by brett01
Is event based menu system a kind of retain mode GUI???can someone help me in
how to start writing them.


Before this topic I've never even seen anyone using immediate/retained mode when talking about GUIs.

One common thing in all GUIs I know and pretty much the whole core of it:
class Widget {   list<Widget*> children;};


A widget has a position/size and other widgets inside. A click/mouse move will go through the tree and check what you're over/what was clicked. Child elements are sorted by "z" (top first if you don't care about transparency, bottom first if you do).

If your buttons have some "EVENT_ID id" they push in a queue when clicked (more like wxWidgets) or "Callback cb" they call (closer to QT with signals and slots) is pretty much up to you and depends on how you're going to use it


Quote:
Original post by Fingers_What are your thoughts? Is it just a question of aesthetics? I will probably need some callbacks anyway in order to allow more advanced customization of GUI objects (e.g. completely replacing how a button is rendered) but I see this as being a different level of user involvement.


I guess it's more than aesthetics, since the most obvious difference is the app controlling when you respond to an event. Well, unless the GUI is in a seperate thread you still have that control to some degree, but if you do the GUI in its own thread it might cause really weird issues if a button-callback can create a soldier whenever it wants. So you would still have to queue that event in some way.

Quote:

Why should a control care how it's drawn?
Why should a button care what happens when it's clicked?
Why should a textbox care what text is in it?


1) Who says it has to? A gui renderer could be responsible to know how a button is drawn.

2) It doesn't. Neither sending an event nor calling a callback means that the button has even the slightest idea a) if and b) how the app will respond to it. We're not talking about "does the button know what to do" but apparently more about "events vs. polling".

3) Because of all the entities in your code the _text_box is the most obvious choice? Would you have a chest have an inventory or the app use a global table to map a chest to some items? Especially if this table ends up being a hardcoded switch/case statement.

I guess the difference is that I don't see the GUI as a part of the application. It's a seperate entity sitting between the app and the user and as such should be "self contained" and not require the app to get its buttons drawn or have the app accessing "internal state" all the time.
f@dzhttp://festini.device-zero.de
Scourage
Scourage
Quote:

One common thing in all GUIs I know and pretty much the whole core of it:
class Widget {   list<Widget*> children;};


A widget has a position/size and other widgets inside. A click/mouse move will go through the tree and check what you're over/what was clicked. Child elements are sorted by "z" (top first if you don't care about transparency, bottom first if you do).



This may be the hardest thing to come to grips with. IMGUI's don't have this "core" part. As far as the user of the API is concerned, there are no "widgets" there are only function calls. There is no allocation, setup, callback registration, cleanup. Just call the button function and if it returns true then it was clicked, so run the code that the button controls. If it's a checkbox, slider, or a text input, just call the function with your application's data member as a parameter and the data member is visualized/manipulated according to the widget type. It's just a simpler way to write GUI's.

If you need to add a new button, just call the function again. No need to create a new widget, add it to the widget tree/hierarchy, register a callback/connect signals and slots, and make sure it gets deallocated.

Cheers,

Bob

[size="3"]Halfway down the trail to Hell...
Trienco
Trienco
So basically IMGUI describes the absense of a GUI, since there is no "entity" that could be considered "the GUI". Everything dynamic about a GUI is replaced by hardcoding everything.

The fact that you say the app needs to actively poll each element all the time sounds just plain wrong to me. For someone trying to "modernize" his design and programming habits that's like promoting horse carriages over cars, simply because they aren't so damn complicated under the hood (also ignoring that you're not supposed to care about what's under the hood).

You also all keep pointing at destroying/cleaning up widgets. Something that has never been an issue in any GUI I used or made, because it's like worrying about cleaning up my local variables on the stack or how my smart pointer will clean up when it goes out of scope. It's done "behind the scenes" and basically "none of your business".




Back to the original question:

Three older tools using selfmade GUIs are still lying around
with code. Neither qualifies for "brilliant".

-SW D6 tool (somewhat experimental and too closely tied to application objects)
-OpenGL MiniGUI (first and least flexible, dialogues need to be hand written)
-Graphplan (simplistic just-for-fun bonus gimmick for some assignment)

There are endless ways to do it and the first question to ask is: how fancy/flexible/dynamic do I need it to be and will I ever use it again after this project?
f@dzhttp://festini.device-zero.de
Scourage
Scourage
Original post by Trienco
So basically IMGUI describes the absense of a GUI, since there is no "entity" that could be considered "the GUI". Everything dynamic about a GUI is replaced by hardcoding everything.



This is not necessarily true. It is quite possible to make the UI functions as scripts and make them dynamic. Blender does this. Every GUI that I've made using Qt and wxWidgets have been hardcoded and not dynamic.

Quote:
Original post by Trienco
The fact that you say the app needs to actively poll each element all the time sounds just plain wrong to me. For someone trying to "modernize" his design and programming habits that's like promoting horse carriages over cars, simply because they aren't so damn complicated under the hood (also ignoring that you're not supposed to care about what's under the hood).



Each element is given some CPU time to check if it needs to change state, do some action, render, whatever widgets need to do. This is going to happen in any widget library.

Quote:
Original post by Trienco
You also all keep pointing at destroying/cleaning up widgets. Something that has never been an issue in any GUI I used or made, because it's like worrying about cleaning up my local variables on the stack or how my smart pointer will clean up when it goes out of scope. It's done "behind the scenes" and basically "none of your business".


Maybe not destroying/cleaning up, but definitely initialization as a precursor step.


Quote:
Original post by Trienco
Back to the original question:

Three older tools using selfmade GUIs are still lying around
with code. Neither qualifies for "brilliant".

-SW D6 tool (somewhat experimental and too closely tied to application objects)
-OpenGL MiniGUI (first and least flexible, dialogues need to be hand written)
-Graphplan (simplistic just-for-fun bonus gimmick for some assignment)

There are endless ways to do it and the first question to ask is: how fancy/flexible/dynamic do I need it to be and will I ever use it again after this project?


Neat examples. There are quite a few ways to implement GUI's. You hit the nail on the head. What are you trying to do and do you want to reuse this capability.

Cheers,

Bob

[size="3"]Halfway down the trail to Hell...
Trienco
Trienco
Quote:
Original post by Scourage
This is not necessarily true. It is quite possible to make the UI functions as scripts and make them dynamic.


True and there are probably other ways to switch out behaviour at runtime.

Quote:
Each element is given some CPU time to check if it needs to change state, do some action, render, whatever widgets need to do. This is going to happen in any widget library.


True, except that unless all elements are animated or otherwise do stuff on their own without user interaction, the difference is that you only touch/update those elements (though rendering is a valid point in some cases, clever usage of the viewport can even avoid redrawing the GUI every frame.. usually not worth the effort though).

Quote:
Maybe not destroying/cleaning up, but definitely initialization as a precursor step.


It's often the only step, unless you will need to enable/disable a button or have a label that frequently needs new text. Most elements are created, set up and patted on the head with an "off you go and do your thing". No need to even store your own pointer/reference to it.

You never "ask" a text edit for its content, because it will send its content whenever it changes. Same goes for all input types and obviously even more for buttons.

Thinking about it, I could reuse existing event handling and factory code, assign each element an ID of any kind I like (int, string, structs) which will then use the ID to have the factory create the according object and place it in the event queue. Since the event dispatcher automatically calls the function registered for this type of event, there wouldn't be a single if or switch involved. To the outside you just "magically" get your handler function called, can pass arbitrary data in your event object and don't need to hack around the fact that having generic callbacks with arbitrary parameters is still a huge mess to implement.

Of course that causes more of that dreaded setup code.

//You could be lazy and just give each event a reference to the object//effectively causing empty classes for most casesstruct EditNameEvent : public Event{   EditNameEvent(const string& oldName, const string& newName);   string oldName, newName;};static const EventFactory::Register<EditNameEvent> dummy(someID);App::onEditName(const EditNameEvent& event){  Player.name = event.newName;}App::App(){  new TextEdit(x,y,w,h,parent,someID);  registerEventHandler(&App::onEditName, this);}App::processEvents(){   while (!eventQueue.empty())   {       handleEvent(eventQueue.front());       eventQueue.pop();   }}TextEdit::onEnterPressed(){   eventQueue.push( EventFactory::create(assignedID) );}


You can probably see the serious downside of this and why using pair(objectID, eventID) or just queue.push(assignedActionID) would be much easier, as my particular "dispatch by type" approach could use any other key as well.

I guess you can tell I prefer avoiding huge "switch(eventID)"-blocks or endless "if this, if that, if the other thing, if, if, ifififififif".
f@dzhttp://festini.device-zero.de
haegarr
haegarr
Hmm. As soon as dynamic is involved you need to store a state. Whether you call this "widget" or not: It contradicts the "no allocation at all" statement, doesn't it? Even if you wrap it in a scripting variable: There _is_ a storage for the state.

Now you can argue that the model (in the sense of the MVC pattern) already stores the state. That is probably true. But what is if the state depends on a dozen model data elements? Is the "visible" state computed on-the-fly again for each rendering? Because, as soon as you store a temporary result close to the visible state you'll get a widget state.

And what happens if you want to populate the screen with N GUI elements with the same behaviour and/or the same look? Do you have to program the functionality N times? Or do you invoke a specific sub-routine N times? But then you again have a kind of widget, and furthur do decentralization (of code).

I can follow the argumentation about avoiding callbacks / listeners, and I also can follow the argumentation of centralizing the control flow (although I disagree with that its advantages outweight), but the other aspects ... I think I still have not seen the clue w/ IMGUI ... :(
CadetUmfer
CadetUmfer
struct EditNameEvent : public Event {   EditNameEvent(const string& oldName, const string& newName);   string oldName, newName;};static const EventFactory::Register<EditNameEvent> dummy(someID);App::onEditName(const EditNameEvent& event) {  Player.name = event.newName;}App::App() {  new TextEdit(x,y,w,h,parent,someID);  registerEventHandler(&App::onEditName, this);}App::processEvents() {   while (!eventQueue.empty()) {       handleEvent(eventQueue.front());       eventQueue.pop();   }}TextEdit::onEnterPressed() {   eventQueue.push( EventFactory::create(assignedID) );}
[/quote]

All of that looks like this for me:
UI::TextBox(UI_ID, Player.name, x, y, w, h);//in my main loopUI::Tick(dt);


There is no callback because the Textbox edits the model (Player.name) directly. So there's no events. No event types. No event factory to create them. No event queue to hold them.

To me, your constant quest to abstract away control flow and turn it into types and callbacks is more confusing and unnecessarily complex for a game UI.

It's similar to the scene graph argument. Some people swear by a retained-mode scene graph. :shrug:
Anthony Umfer
Trienco
Trienco
Original post by CadetUmfer
All of that looks like this for me:
UI::TextBox(UI_ID, Player.name, x, y, w, h);//in my main loopUI::Tick(dt);

[/quote]

Well, you are conveniently leaving out all the code and logic you obviously didn't encapsulate away in a GUI library. In the same vein I could post the code for the next smash hit video game: while(1) update();

Your tick function contains some interesting parts, especially the part where you poll all your objects, how you determine if it was clicked (there must be _some_ common code, unless you copy/paste endless amounts of "if mouse inside me and button down"-code into a gazillion "updateTextBox735()" functions).

Is it really less code, just because you replace the above registrations with switch() here, if() there and a potentially loooooooooong UI::Tick function?
f@dzhttp://festini.device-zero.de

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.