Sign in to follow this  
Toolmaker

Writing custom UI system for game...

Recommended Posts

I'm currently in the process of writing a UI system for my game. While most of it works, I'm concerned about handling the mouse events throughout controls. For instance, I need to know which control my mouse is over when I click or move the mouse(So I can send enter/leave/mouse messages). Right now, i'm having a list of all Windows in game, and check if the mouse is in one of these Windows. If it is, I send a message to the Window about what happened(MouseEnter, MouseLeave, MouseMove, MouseDown/Up/Click). However, this is only the Window. Each window can have multiple siblings, which in return can have siblings of themselfs. How would I go in notifying these siblings about said action too? I technically could add all controls I have to a list and walk this list, check if the mouse is over any of those controls, and from there-on walk the list up via the Parent property, or have the event cascade down from the WindowManager to the control and have the control pass it on to it's siblings. What would be most efficient in this case? Toolmaker

Share this post


Link to post
Share on other sites
I've been pondering the idea of a custom GUI of late myself, and while I'm as yet to write any code personally I'd go from the parent down to the children.

This effectively gives you an 'early out' when you click on a parent but don't have a child under it which needs to respond under the click.

Share this post


Link to post
Share on other sites
I have the widgets in a n-tree and recurse through the tree hunting for the last object which would catch the event. It's certainly good/responsive enough, even with a few thousand widgets in the scene. If you place the constraint that child rectangles must be within their parent's that would allow much faster handling since you can not recurse if the mouse misses the parent. I don't place that constraint and things are still plenty fast.

Share this post


Link to post
Share on other sites
I do it the way Telastyn does. There are materials on GUI design on the web, and you should look at how existing GUI frameworks do stuff.

Some events everyone gets, whether the mouse is over them or not. Such as mouse motion, and mouse button releases. In fact, the only event that should really be restricted to the hover widget is mouse button down. You'll understand why if you ever write a scrollbar widget.

Share this post


Link to post
Share on other sites
In my GUI system every actual control inherits from ComponentContainer and Component. Every ComponentContainer holds a list of direct child Components.

If i detect mouse events inside a Component i call upon ComponentContainers routine to find the child below the mouse. If there is a child Component found the mouse event is modified (relativate position) and passed on. If there is no child found the message is handled in the Component.

The GUI top parent is simply a ComponentContainer without the Component part. This allows me to simply pass the mouse event to the top parent. From there on it's passed down in the hierarchy.

Share this post


Link to post
Share on other sites
Quote:
Original post by Deyja
Some events everyone gets, whether the mouse is over them or not. Such as mouse motion, and mouse button releases. In fact, the only event that should really be restricted to the hover widget is mouse button down. You'll understand why if you ever write a scrollbar widget.


Care to expand upon that a bit?

Share this post


Link to post
Share on other sites
Scrollbars almost always let you move the mouse to the side while your scrolling. This would mean that the mouse movement even is now off of the scrollbar. If you don't let the scrollbar handle movement events that are out of its bounds, it won't know this and will stop working the moment you move your mouse out of the thin bar. And if it can't handle mouse releases, it's even worse. It'd never know that the mouse was released, and so it could start being dragged again if you move your mouse over the scrollbar (eg: I drag the bar a bit, move my mouse to the side, release the mouse button. Later on, I click and hold down somewhere else and then move my mouse over the scrollbar and the scrollbar would start moving again without me ever clicking on it).

Share this post


Link to post
Share on other sites
I use a hierarchy like this in my engine: CActor -> CGuiWidget -> CButtonWidget (or CEditWidget, CStaticPictureWidget, etc.) -> (custom user controls).

Event handling is still in progress, maybe I'll make a post on my gui once it's finished. BTW, the icon for the GUI editor in my map editor is... gooey... a bottle of glue :)

Share this post


Link to post
Share on other sites
My setup is pretty similar to what has already been described, a sort of scene graph for widgets. My inheritance hierarchy is like this: Rectangle->Widget->Container->Panel. "Normal" widgets such as buttons inherit directly from Widget. Container keeps track of which child has keyboard focus and which child is being hovered over, and delegates input events to its children. At the top of the tree I have a Desktop widget covering the whole display which either passes input events on or handles the special cases for the Console window or does general-case input to the game, like keyboard shortcuts or picking.

Reading over this thread, it seems that I'm going to have some problems implementing scroll bars, so I might have to re-design some of the event passing mechanisms. But the general idea of a widget tree still holds, so I'll add my voice to the others recommending it.

Share this post


Link to post
Share on other sites
Quote:
Care to expand upon that a bit?


Ezbez got it. It can also be helpful to maintain a record of which widget the mouse is over, and of which widget the mouse was over when the button was depressed. This allows you to implement a click event when the mouse is released, and only call it if depressed on == hover.

Share this post


Link to post
Share on other sites
Due to easter I haven't been able to follow this thread, but the thread does provide a few good ideas.

I like the example of the scrollbar, and it's good to know you people mentioned it.

However, how do you keep record of the widgets? A huge tree which holds all the parents and siblings, or just keep a reference to the top-level parents(ie, the Windows) and pass the events on from those? Or do you have, say a quad-tree, with a link to all the widgets and traverse that?

Right now, I use this:
Control -> Window
-> Desktop Window(Basically the parent of all Windows)

The WindowManager basically calls CascadeMouseEvent() on DesktopWindow, which will check which Window has the mouse over and then let's the mouse event cascade into that one.

However, if I want to inform the controls about all events(Except button down/up), it would perhaps be more efficient to have the DesktopWindow handle this, because in that case, the DesktopWindow detects which control gets the actual message, instead of each control going the same math.

Toolmaker

Share this post


Link to post
Share on other sites
Quote:
Original post by Toolmaker
However, how do you keep record of the widgets? A huge tree which holds all the parents and siblings, or just keep a reference to the top-level parents(ie, the Windows) and pass the events on from those? Or do you have, say a quad-tree, with a link to all the widgets and traverse that?


I keep a reference to the desktop in the UIManager. For input, I register a callback once: inputManager.setInputListener(desktop)

Although I am a bit inconsistent because to render the UI I call uiManager.update() which in turn calls desktop.paint() (which calls paint() recursively for all widgets)

Share this post


Link to post
Share on other sites
Quote:
Original post by lightbringer
Quote:
Original post by Toolmaker
However, how do you keep record of the widgets? A huge tree which holds all the parents and siblings, or just keep a reference to the top-level parents(ie, the Windows) and pass the events on from those? Or do you have, say a quad-tree, with a link to all the widgets and traverse that?


I keep a reference to the desktop in the UIManager. For input, I register a callback once: inputManager.setInputListener(desktop)

Although I am a bit inconsistent because to render the UI I call uiManager.update() which in turn calls desktop.paint() (which calls paint() recursively for all widgets)


Right now, my WindowManager receives all the mouse updates(I'm working in C#, using events for the normal mouse input stuff), and in WindowManager.Update(), I process the latest state(down-side: there's a chance I'll miss a click, so I might change it a bit to buffer any mouse clicks, since mouse movements just send the new X,Y coordinates).

So, I could just send all mouse events down into the DesktopWindow, which will send the events down into Controls and these will recursively send the events further down.

For Up/down/Click events, I can find the window which the mouse is hovering over, and then ask this Control to find out which of it's siblings the mouse is actually over(Again, a recursive function, which will eventually return null when none is found).

Toolmaker

Share this post


Link to post
Share on other sites
Quote:
Original post by Toolmaker
However, how do you keep record of the widgets?



class BaseWidget{
public BaseWidget Parent;
public List<BaseWidget> Children;
public InputBindings InputHandler;
public Rectangle{ get; }

// ...
}



There's then one widget which acts as the root of the tree and has no parent. (and in my setup, contains window info) The root is then the only thing kept explicitly by the application.

This provides enough context for the input code to pick which widget is supposed to receive the input, which is then triggered.

Share this post


Link to post
Share on other sites
Quote:
Original post by Telastyn
Quote:
Original post by Toolmaker
However, how do you keep record of the widgets?


*** Source Snippet Removed ***

There's then one widget which acts as the root of the tree and has no parent. (and in my setup, contains window info) The root is then the only thing kept explicitly by the application.

This provides enough context for the input code to pick which widget is supposed to receive the input, which is then triggered.


That's exactly what I'm doing right now, except i've called it DesktopWindow, and I then traverse the list of controls.

Toolmaker

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