Game engine gui design

Started by
22 comments, last by Juliean 11 years, 8 months ago
Ok, things are much more clear to me now. Lets go through it:


f you were to add an addChild() method to your EditorObject class which accepts another EditorObject, and if you were to maintain an actual list of children inside an EditorObject you could easily update your entire gui with only a single update call on the root widget. Within each update call in a widget you would traverse the list of children and do an update call on each of them individually, which gives you a nice recursive way of updating the gui.
Managing your gui in such a tree structure also makes your gui much easier to traverse from a programmer's point of view, as having access to a parent widget means having access to the entire subtree which that parent is root of.
I remain with my statement that using those super-methods is pretty much a hack.
[/quote]

I actually have a vector<EditorObject*> included to the EditorObject-class that I can add child objects too. I still felt like hardcoding certain consistant elements like the three buttons for the scrollbar makes things easier to work with. I'll have to overload the UpdateSuper()-method, but thats not a huge problem, since there is not such a huge amount of base objects derived from EditorObjects anymore after I got rid of the redundand ones..

Oh, by the way, I figured that what you mean that my update-method does is indeed the Update_Super-method, except I'm not passing the parents coordinates to the update-method but with SetSuperX() and SetSuperY(). Update_Super would just do the same thing for the (hard-coded) children.

Maybe I'm misinterpreting the way you use your update function in your gui elements, if that's the case just let me know. Right now, since I don't see any other way how you're updating your actual logic to manipulate your back-end data (eg. how does your scrollable window know that your scrollbar has been updated?), I'm assuming you're doing this in your update function.[/quote]

My windows knows about it from the return of Update(). If its true, it has been updated, So if m_pScrollbar()->Update() returns true, it calls m_pScrollbar->GetNowValue() to get the actual value and adjusts the content to that. It does that by setting the m_iOffSetX or Y variable, that is used in UpdateSuper() to set the according contents position.


If you wanted to do all of this purely with inheritance you'd have to create a button class which would handle this entire process in its update system. Also, if you wanted your button to be able to control and represent multiple sets of data at once you'd have to create a separate case for that as well, whereas MVC allows a single controller to manipulate and a single view to represent/observer multiple models.[/quote]

Well not quite, at least in my system. The Update()-method of the button determines its state: clicked, hold, hold+over, released, released+over (=activated). it returns true if any of these states apply, and the holding object then decides what to do with that, by checking if it has been activated. The object holding it might then call Down() to fix the button, Up() to release it and SetActive(bool) to make it unclickable/clickable. This is, example given, how I handle my tileset-toolbar right now:


EditorButton* pButton = m_pToolbar->GetActiveButton();
if(pButton == NULL || !pButton->Pressed() || !pButton->Over(Input::GetMousePos()))
return false;
switch(pButton->GetID())
{
case 0:
(*m_ppBrush)->SetErase();
break;
case 1:
{
pButton->Down();
PencilBrush* pPencilBrush = new PencilBrush(**m_ppBrush);
delete *m_ppBrush;
*m_ppBrush = pPencilBrush;
m_pToolbar->GetButton(2)->Up();
m_pToolbar->GetButton(3)->Up();
break;
}
case 2:
{
pButton->Down();
RectBrush* pRectBrush = new RectBrush(**m_ppBrush);
delete *m_ppBrush;
*m_ppBrush = pRectBrush;
m_pToolbar->GetButton(1)->Up();
m_pToolbar->GetButton(3)->Up();
break;
}
case 3:
{
pButton->Down();
BucketBrush* pBucketBrush = new BucketBrush(**m_ppBrush);
delete *m_ppBrush;
*m_ppBrush = pBucketBrush;
m_pToolbar->GetButton(1)->Up();
m_pToolbar->GetButton(2)->Up();
break;
}
}


So other then having that a case-statement and knowing the button by ID (it's not a big deal), I can just call new Toolbar, push back a few new buttons, and that code above is all I need to get it to work.

--- MVC-explanation ---

Ok, so I've read what you explained, and MVC definately sounds like a concept that does fit perfecly here. I don't know how I'd go about implementing it right now but thats maybe because I haven't worked with messaging-systems at all until now. But, before I go ahead and screw my current approach, I'd like to ask once again: Do I really get that much advantage of using it? Sorry, I don't mean to be annoying or anything, I just like to question things. There are many worst-case-scenarios you described, which happend to me in my first try on writing a map editor years ago. I've overused OOP to an extent that it wasn't workable with, like you described, and the whole thing wasn't even working well. It didn't have the featureset I already have by this time, though the older one took much longer to develop. Maybe I didn't emphasise that enough, but the current system works flawlessly, so I'm not asking for an alternative because my current approach is unworkable, no, it works so well that I'm looking for a way of making things even easier to work with. So, in a nutshell, here is how the whole update-routine works (or will work after I got rid of the EditorWindow-derived classes):

- Main frame calls Update() on all of its widgets
- Update() first sets traversal coordinates for all its children by calling UpdateSuper() (sry, I just realized how confiusing this may appeal. I'll try to improve it)
- next, Update()- all of the children
- If widget without children is hit: perform a self-update (e.g. button deciding what state is in)
- If widget was activated, return true, else false
- If Update()-returns true: set children as active, perform destinctive actions (eg. set offset variable when scrollbar has changed), skip other children and self-update, else proceed

So now at this point I'm not sure how to proceed. Thinking about it, isn't that almost what MVC tells? Except that I don't let an extern controller regulate the basic actions like a button being clicked, but.. whats the point here? Every button will work the same so why would I want an extern button-controller when I can simply let the button handle itself? And again, I'm not talking about how the button manipulates that data, I'm just talking about the buttons states. But hold onm a second. Say if I where to make my widgets able of registering a controller, to that the button sends the information that it was activated, and let that controller decide how to manipulate the back-end data. Wouldn't that be almost what you were talking about? That would be my plan for now, composing the destinctive windows (tileset,tilemap), out of one or more widgets, having a controller, registering that controller to the widget and having it send information back to the controller. How does that sound, in combination with my above update procedure?
Advertisement

Say if I where to make my widgets able of registering a controller, to that the button sends the information that it was activated, and let that controller decide how to manipulate the back-end data.


That's basically the concept of using a controller yes, the button does not need to know what kind of controller it's maintaining, only that it is a controller, so any type of controller which could control any kind of model could be assigned to this button.


My windows knows about it from the return of Update(). If its true, it has been updated, So if m_pScrollbar()->Update() returns true, it calls m_pScrollbar->GetNowValue() to get the actual value and adjusts the content to that. It does that by setting the m_iOffSetX or Y variable, that is used in UpdateSuper() to set the according contents position.


In this case a window must explicitly know that it has a scrollbar which is manipulating it, which is not something you want in a flexible gui. What if you wanted to add a button to your window which scrolled down to a certain piece of content? With this method the window object would now also have to poll this button on each update to see whether it was clicked and update itself accordingly. Doesn't sound all too flexible, does it?
The same applies for the toolbar example you gave. Adding a button to the toolbar would require you to go in and edit the toolbar code to allow this new button to perform a certain task. With MVC you can add any generic button to any generic toolbar, and you can register a specific controller to that new button which completely defines its behaviour. No need to go in and edit your classes each time you want to add a certain bit of functionality for a specific use case.
The result of not having to go in and modify your code for widget behaviour is that you basically create a very simple environment where you can just compose your GUI by using completely generic widgets (buttons, scrollbars, images, windows, etc.), after which you assign models to them so they display the data they need to display and controllers so they can manipulate data. If you really wanted to you could do this entirely in a visual editor without once having to touch any piece of code.


I stepped away from this to hard-code the objects and overload the UpdateSuper()-method. This was most definately unevadable due to my design, since without any messaging system, I pretty much depend on specific objects to being certain methods called on directly.


This is a design flaw, and hardcoding everything in and making these objects which are supposed to be decoupled from one another explicitly know about eachother is not a solution. In the case of a bad design you should modify your design to something that is extensible and which works without hacks instead of adding more hacks to make the original design work.
Some form of messaging will be required eventually in a GUI system, so better to design it properly now instead of having to go back and adjust your design later on. MVC can provide a solution here since broadcasting of state changes and events is automatically implied as soon as one occurs.

Your current system might work now, but I can guarantee you that extending it and adding complex interactions to it will become a huge pain, both to implement and maintain.

I gets all your texture budgets!

Ok, I've almost got it.

That's basically the concept of using a controller yes, the button does not need to know what kind of controller it's maintaining, only that it is a controller, so any type of controller which could control any kind of model could be assigned to this button.[/quote]

So I'd design a generic controller interface, but how would I go about sending messages to it? My first intuitive would be to write different generic message-method like button_clicked, scrollbar_moved, and overload these, but it sounds again a little messy. Maybe a general send_message()-method where I pass information on what type of object sent it? Or anything completely different? Before stepping ahead and starting implenting it, I want to make sure. And I do inherit from the generic controller for the certain use purposes, right, or is there still something different to it?


In this case a window must explicitly know that it has a scrollbar which is manipulating it, which is not something you want in a flexible gui. What if you wanted to add a button to your window which scrolled down to a certain piece of content? With this method the window object would now also have to poll this button on each update to see whether it was clicked and update itself accordingly. Doesn't sound all too flexible, does it?
[/quote]


Flexibility wasn't my main purpose. Aside of my design flaws, I just made some deliberate simplifications as e.g. every window has two scrollbars (which might there or not) and a toolbar, etc.. well while I'm at it I might make it more flexible as well.


The same applies for the toolbar example you gave. Adding a button to the toolbar would require you to go in and edit the toolbar code to allow this new button to perform a certain task. With MVC you can add any generic button to any generic toolbar, and you can register a specific controller to that new button which completely defines its behaviour. No need to go in and edit your classes each time you want to add a certain bit of functionality for a specific use case.
[/quote]


Not quite, as in the code I posted above (it's in the redundand WindowTileset-class but still) the button actions are handled, and my toolbar stores a vector<EditorButton*>, so all I'd really have to do is change that type of code. But since I might want to add other objects to the toolbar as well, I should make it more general, too.

This is a design flaw, and hardcoding everything in and making these objects which are supposed to be decoupled from one another explicitly know about eachother is not a solution. In the case of a bad design you should modify your design to something that is extensible and which works without hacks instead of adding more hacks to make the original design work.
Some form of messaging will be required eventually in a GUI system, so better to design it properly now instead of having to go back and adjust your design later on. MVC can provide a solution here since broadcasting of state changes and events is automatically implied as soon as one occurs.[/quote]

Ah, I slowly realize how much neater to work such a generic system was. Ok, so what if I made this:

- First, i call MainFrame->Update(). Update() now doesn't do anything more than traverse all the child objects, and so on.
- If the main controller receives any event (mouse-move, mouse-click) it will check recursively if an widget is underneath the mouse OR marked active (if I e.g. clicked on a button and hold the mouse, I don't want anything else happen unless the mouse is released, and even if it is, the release-message should still be sent to the button original held. Do I really need to keep like a extra pointer if an object is active and check that first, or is there any way to automatice that?) and send a message to the widget, like onClick(). (By the way, can I just send messages as a method, so therefore call m_vObjects[X].onClick() or do I need to use some more abstrusive design?)
- The object, eg. button does all self-management it needs (changing texture to represent pressed state, etc..) and then sents the controller an onClick()-message -> see question at the top.

Sounds good, at least to me. Well I'd be glad if you could explain to me how I should communicate with the controller, other than that I think I'll go that route, shouldn't I?
Radikalizm went through a lot on showing you MVC examples to maintain a graphical interface and its parts, so I will tell you why it would make sense to move more towards components and avoid overuse of inheritance.


@CC Ricers:
For instance, a clickable menu entry can be an aggregate of a TextEntry and a Button


A scrollbar (just the bar itself, not the screen it scrolls) would be a Button and a Draggable entity with vertical/horizontal constraints added to it[/quote]

Thats just how I was planning to handle things, except that my scrollbar still inherits from EditorObject, because, after all, isn't this a "base object" too? This drastically reduces the amount of code needed as well. Maybe I should show you what EditorObject looks like:

*snip*

So EditorObject describes a general object with variables that apply to every gui element. (position,size, a sprite object). The whole Super-thingy is for traversion, it spares me having to pass the holding objects x/y-coordinates all the time. ProjectPosition() is a helper function that outputs the mouse coordinates relative to the objects position, saves me a lot of math.

So I quess everyone would agree that I should at least inherit these classes from that:

EditorImage
EditorButton

If not, why?

If yes, why shouldn't I, example given, also inherit the Scrollbar from this?

The scrollbar, while being basically a composition of three buttons and a background-image, does have its own coordinates, destinctive size, needs to be drawn in relation to other objects, etc.. so whats exactly wrong about that or, better said: What are the benefits of composition here? What is such a huge improvement from having an extern controller handling the scrollbars usage to just having the Update() method do it? This is what, for example, the inherited scrollbar-class looks like:

*snip*

Except that Setup() and SetButtons() could/should be moved to the constructor: Again, why would I want to composite instead of inherit here? The scrollbar class uses everything from its super-class, and just extents it. Do I really have such a misunderstanding of OOP-basics or is it some matter of opinion whether you see a scrollbar a base object here?

So I know that my EditorWindow->WindowTileset/WindowTilemap-classes are bad and I'll going to replace that with some composited objects here, but I really see to fail the point for such a basic objects as a scrollbar.

[/quote]

It's true that eventually all your widgets need to have a base class in common- if the intent is to have a separate GUI system go through all the widgets and draw/update them each time, the system needs to know what counts as a widget, and be able to register objects of the Widget type.

What I don't favor is putting bunch of variables that are sprawling over the place inside the Widget class, when they can be split up and contained in their own classes or structures, which you can then use with more flexibility. For example, width, height and X and Y position can be stored in a Rectangle structure. The extent to how you define the visual appearance is up to you. Do you want the UI system to use one skin, or maybe give the ability to mix several skins, with some taking priority over the "default" skin? Whatever the case may be, this should be separate from the classes that define behavior.

Also, you have to see what makes each widget fundamentally unique. What makes a button a button? It's certainly not its size and position because all widgets have those. A button, like all other UI elements, is defined by its behavior. The Button class need not know it's position, just whether it's been clicked on, or not, and if clicked, trigger an event that would be passed up to the UI system to get the proper response.

The inheritance to a base object would exist simply to make it possible to queue all widgets, extract necessary information that a particular subsystem would need (like a sound player or graphics renderer), and have those subsystems do whatever they need with the information. The rendering system should not care what kind of widget the Button is, or even that it's a widget. You should just pass along to it the UI skin parameters to tell it what the element looks like and the Rectangle object to tell it where to draw the element.

As I mentioned before, I'm not making a terribly complex UI- all I need are some short menus, and a couple of buttons and dialog boxes here and there. But they all share a common objects through composition (Buttons, TextEntries and Backgrounds), and share a skin object that resides in the UI/screen manager, for a more consistent look throughout the game. Also, admittedly they take care of drawing themselves for now, so that's a big "don't do" on my part that I have to fix. But If I needed something more complex (and I was designing it myself), I would design a more elegant solution.

New game in progress: Project SeedWorld

My development blog: Electronic Meteor

It does indeed sound like you have the hang of it smile.png

One example of communicating with a controller is letting a widget maintain a list of controllers. When an onClick event occurs (so your button's onClick() method is called) for example you could iterate over your list of controllers and notify each of them that they should perform their designated action. This will automatically start the chain reaction of updating both model and view.

The Qt library does this in a slightly different way which you already mentioned in some previous post I believe. They use the signal-slot mechanism to connect certain triggers or signals from a widget to a function or slot. This does of course require their own meta-object compiler with some custom C++ extensions, so unless you're ok with providing a quite advanced pre-compilation step for your project this isn't really an option.

In C# something similar is implemented with delegates which can be linked to events. Something similar could probably be built in C++, but I'd need to do some research on that before I could give you any details.

I gets all your texture budgets!

@CC Ricers:

What I don't favor is putting bunch of variables that are sprawling over the place inside the Widget class, when they can be split up and contained in their own classes or structures, which you can then use with more flexibility. For example, width, height and X and Y position can be stored in a Rectangle structure. The extent to how you define the visual appearance is up to you. Do you want the UI system to use one skin, or maybe give the ability to mix several skins, with some taking priority over the "default" skin? Whatever the case may be, this should be separate from the classes that define behavior.[/quote]

Ok, I agree, I might want to put x, y, width and height into some sort of structure. Talking about the skin, I was just passing a texture pointer to the element, that represented the skin. Having the UI system itself handling the skins seems like a good idea, though. Didn't think about that.

Also, you have to see what makes each widget fundamentally unique. What makes a button a button? It's certainly not its size and position because all widgets have those. A button, like all other UI elements, is defined by its behavior. The Button class need not know it's position, just whether it's been clicked on, or not, and if clicked, trigger an event that would be passed up to the UI system to get the proper response.[/quote]

So you are talking about the button as being just the controller talking about MVC, right? Ah, I think I get it. Does that mean I'd have my baseclass EditorObject, which acts as a view, and can hold various amounts of controllers as well as the model. It accepts user inputs, but only redirects them to the controllers, which themself change the data in the model. So a button would be a normal Widget with a special controller that handles the button-specific tasks. Right? My head slowly starts to ache :(

One thing that I, however, really don't understand, is what the model exactly is. Is it some sort of template, or a certain data type that i define for each widget? Or is it like the buttons back-texture and image/text?

@Radikalizm:

One example of communicating with a controller is letting a widget maintain a list of controllers. When an onClick event occurs (so your button's onClick() method is called) for example you could iterate over your list of controllers and notify each of them that they should perform their designated action. This will automatically start the chain reaction of updating both model and view.[/quote]

Ok, that seems legit, but what I meant more specifically is how exactly do I tell the controller what exactly happend? Should I maintain a notify function for all widgets possible actions, like onButtonClick()?


At that point, though I think I still need a bit more, I want to thank you guys already for your help. I'll wait for your next answers, in the meanwhile I'll try to pseudo-code what I think this should look like..

Should I maintain a notify function for all widgets possible actions, like onButtonClick()?


I wouldn't do that since you'd have to update your code in 2 locations whenever you want to add a definition for a widget, which could become quite a mess after a while, and which goes against the principle of self-containment in OOD.

Solving this problem can indeed be tricky, but it mostly depends on personal preference as there are many ways to solve it. You could design your controllers to accept a generic message structure which contains event arguments as a parameter for their update function (or whatever you want to call it). The burden of parsing this message and translating it into something which makes sense would then be on the controller.
Pressing or releasing a button could for example pass on a message containing a boolean value with the pressed state of the button. If the controller which receives this message can work with a single boolean value it can continue on happily, if it can't however you could let it print an error message, or you could just let it ignore the message.

I gets all your texture budgets!

Ok, thanks, I'll try figure out what fits best once I've gotten that far.

So I gave it a try to somewhat code it out on a basic level, and that what I've gotten (I actually tried to cover the real MVC-pattern this time, if it doesn't work out that well, I'll go back to my old pattern):


class View
{
public:
View(Model* pModel);
virtual ~View(void);

void OnClick(void);
void OnOver(void);
void OnRelease(void);

void AddChild(Widget* pWidget);
void AddController(Controller*);

void Update(int x, int y); // traversiallyupdate childs
virtual void Draw(void);

protected:

vector<Controller*> m_vControllers
vector<View*> m_vChilds;
Model* m_pModel;
};


So I'd have my widget some generic events like OnClick, an Update() and Draw()-method, a number of controllers, as well as some childs and the model its holding. I'm still confused about the model part, but I'll come to that later. Here is the controller:


class Controller
{
public:

Controller(Model* pModel);
virtual ~Controller(void);

virtual void SendMessage(wstring lpString);

protected:

Model* m_pModel;
}



Well this basically knows that model it is holding, and has a method of sending a message to it. Thats all I need, if I understand that right. Now to the model


class Model
{
public:

Model();//??
virtual ~Model(void);

protected:
//??
}



I've got no idea how to design the model in a generic pattern. Really, I can't even come up with any example. How would I design this in a way that I don't have to inherit from this class for every set of data I want to display, and not have to inherit widget accordingly? This would kill the whole purpose, I belive.

Some more questions:

- Where does the position/size goes to? The view, or the model?
- Do I inherit my Widgets from View, or do I really compose them out of views,models and controllers (by having a seperate widget class from which I inherit) or it the View actually the Widget?

My main problem however is the model. I can't figure that part out. I know what it basically is, but I don't know how I can code it.
e: did misread much, edit post


class View
{
...
void AddController(Controller*);
...
}

I wouldn't do that, generally the view allows the controller to register "events", the view doesn't know about the controller directly.


I've got no idea how to design the model in a generic pattern. Really, I can't even come up with any example. How would I design this in a way that I don't have to inherit from this class for every set of data I want to display, and not have to inherit widget accordingly? This would kill the whole purpose, I belive.

Well, I think what you want to do is build your own gui framework. This means you build templates for the View. Templates for any controller / model is not a task of the windowing library afaik.
This means for the windowing library you need a class design for your gui elements and a messaging system, implementing the MVC pattern is not up to the library.

What do you want to do exactly, build a map editor? build a Settings- / Gamemenu build an in-game HUD?

If possible, use a windowing library, what you are doing here is trying to reinvent the wheel.



What I don't favor is putting bunch of variables that are sprawling over the place inside the Widget class, when they can be split up and contained in their own classes or structures, which you can then use with more flexibility

While I agree that it makes sense to put that in a structure, I don't agree that it gives you more flexibilty. What should be so more flexible about this approach?
Project: Project
Setting fire to these damn cows one entry at a time!
Thank you,

but you misread what the main purpose was. Totally understandable, given that wall of posts, I'll tell you what I want to have. The main purpose of this is to bouilt a game editor. No only map, I would call it that. I decided to go the other way round this time and instead of implement the game and build the editor afterwards, making testing hard at first. I'd built the game editor first and only write the the pieces of game the editor is able to display/alter.

I wouldn't do that, generally the view allows the controller to register "events", the view doesn't know about the controller directly.[/quote]

How would I do that? I can think about it using function pointers, but this whole topic is new to me so I'd rather go with registering the controller directely, unless there is some huge drawback.


If possible, use a windowing library, what you are doing here is trying to reinvent the wheel.

[/quote]

I really don't want to do that, as I'd like to get an understanding of how things work under the hood. You may call it a waste of time but I've learned quite a much only the last 24 hours, which might become usefull again later on.


Well, I think what you want to do is build your own gui framework. This means you build templates for the View. Templates for any controller / model is not a task of the windowing library afaik.
This means for the windowing library you need a class design for your gui elements and a messaging system, implementing the MVC pattern is not up to the library.
[/quote]

Still, I need some basic implentation of a model, right? I've implemented the basics of this, and it really is much easier and cleaner then my last approach, but.. what about the model? Is it really a class like this:


class Model
{
Model();
virtual ~Model();

protected:
int m_X, m_Y, m_Width, m_iHeight;
}


That like here holds the position and size of the widget, and from which I inherit in every special use case? Please, I'd really need an answer here. I've searched high and low but couldn't find a single example mentioning a concrete example of the model for that use case.

This topic is closed to new replies.

Advertisement