GUI planning: composition vs inheritance

Started by
21 comments, last by SyncViews 5 years, 1 month ago
5 hours ago, maltman said:

Well the way Hodgman described it is how I was thinking. Sort of treating all the behaviors of the window/entities like they were components to attach like legos.

Ah, okay, I was reading your opening post as saying you'd thought of using behaviours and then rejected that idea but I see it can be read another way :)

Advertisement

Ive made a AComponent class which is a base class for everything from buttons to windows, it stores position, size color, name etc

And defined different classes like

struct TWindow : public AComponent{};

struct TButton : public AComponent{};

With their own functions

There are alo virtual functions for drawing, mouse interactions etc.

49 minutes ago, Nypyren said:

Source:  I have extensive experience using Unity's IMGUI system for complex in-Editor tool windows, and have to constantly work around all of these headaches because Unity's Editor code still has no way to let me write retained mode UI.  Retained mode I've used include Unity's UGUI system (which unfortunately is only usable while the game is running, not in the Editor) and .Net's WinForms and WPF systems.  The retained mode systems are far better for creating complex UI than IMGUI is.  I haven't used any IMGUI systems other than Unity's, but I have never seen an article describing and implementation which works any differently or solves any of those problems.

For a counter experience, I've recently started rewriting my games UI from a retained model, based on the HTML DOM style (libRocket) to the IMGUI style (Dear ImGui for developer/editor screens, Nuklear for game/user UI).

My new IMGUI code is actually way faster than my old retained code, somehow. The complexity of the general purpose DOM, with virtual/inheritance based nodes (and the abysmal memory access behaviour that comes with that) was somehow about 10x more expensive than the IMGUI book-keeping, even though it involves lots of hash lookups, etc ?

The game UI I think is an especially good fit for immediate, as in the worst case, they're changing every single frame anyway, so the ImGui that's optimised for that situation is faster than a retained GUI that puts work into trying to cache layouts. For something like a text editor you'd definitely get massive wins from caching your text layouts though.

I also find it much quicker and more programmer friendly to actually write UI code now. I used to avoid writing developer UIs because it was a painful exercise every time. Now I find myself making a tweaking and visualisation GUI for every single gameplay system because it's a frictionless task now.

8 hours ago, Hodgman said:

For a counter experience, I've recently started rewriting my games UI from a retained model, based on the HTML DOM style (libRocket) to the IMGUI style (Dear ImGui for developer/editor screens, Nuklear for game/user UI).

So it's possible that Unity's IMGUI system is just abysmally bad, then, and I should give alternatives a chance.

At what scale are your UIs?  Do you have scrolling panels with thousands of UI elements in them?

Well I've hit an impasse. I like my idea for a tree style system of entities, however it has revealed a weakness in me: I know nothing about trees, traversing trees, managing trees. I think I should continue with this style because so far it has been a great learning tool for me. I've never used recursion so much as I have working on this.

I have trouble doing recursion with classes since "*this" seems to only point to the base classes and not the classes accessed via recursion. Not sure how to get around that. 

Anyways lots of fun, lots of fun. I'll probably start another side project to do more gamey style crap maybe with a multiplayer element, but this will still be an ongoing project for me as I learn more about recursiveness and trees.

6 hours ago, maltman said:

Well I've hit an impasse. I like my idea for a tree style system of entities, however it has revealed a weakness in me: I know nothing about trees, traversing trees, managing trees.

Which part exactly? If each window has a list of children, the iteration order in the basic case is fairly straight forward for the basics (things like select / dropdown boxes need a little thinking because if they are a straight child elements they extend outside their parents boundary box and overlap other stuff, and you must handle that for mouse interaction).

7 hours ago, maltman said:

I have trouble doing recursion with classes since "*this" seems to only point to the base classes and not the classes accessed via recursion. Not sure how to get around that. 

On 3/2/2019 at 8:23 PM, maltman said:

This class will itself contain a vector of its children Windows...

 

Not really following, what is your object in this case? For recursion Id expect it to be the current node, "child" in "child.Update()".


WindowElement.Update()
  foreach child in children do
    child.Update()

If the thing you are recursing is only data, a variable can easily take the place of "this".


Gui.Update()
  UpdateElement(root)

Gui.UpdateElement(node)
  foreach child in node.children do
    UpdateElement(child)

Where you have separate component logic, then the component either wants to store a reference to its owning element, or have one passed to it on each event/update.

I can also provide some counter evidence to IMGUI being bad.

This is my editor/engine written entirely in IMGUI, my own library that I've rolled over the last few years. With appropriate optimisations the UI is extremely fast to render:

 

image.thumb.png.f7458c35cc734fef8944e9b91e5b8902.png

4 hours ago, David Lovegrove said:

I can also provide some counter evidence to IMGUI being bad.

This is my editor/engine written entirely in IMGUI, my own library that I've rolled over the last few years. With appropriate optimisations the UI is extremely fast to render:

I think it is a matter of scale. Most games don't have a GUI anywhere near that complex. In my experience as soon as you need a lot of scrolling, layouts, overlapping elements (e.g. drop downs obscuring buttons below them), determining container size dynamically (which then further effects layout), etc. its a whole jump in complexity however you are going about it.

And with such a complex GUI, 99% of it is basically doing nothing most of the time and you can optimise for that. Which I don't think I have seen say Unity IMGUI do, least not what I have seen in Unity where it seemed to build the entire thing every frame, check every button state, calculate every position, etc.?

On the other hand, if you want to render one of those quick-chat spinner things, I can see why allocating a bunch of "spinner-elements" and event handlers, running a layout engine, etc. is not desired, even as far as a bunch of buttons for selected units in an RTS.

@SyncViews  Your post is pretty enlightening for me. I didn't think of using a for loop like that. Recursion is still pretty confusing for me. That is part of why I am doing this project on a GUI before a game. GUI programming is at least convergent, you know what you want you just have to figure out how to get it. Game programming seems to me a bit more complex and open ended.

 

I am not very interested in the immediate mode GUI after learning about them. I want to make a GUI library which inflicts the least amount of mess upon the client codebase as possible. Something clean and rather opaque. Also I see nothing wrong with caching data. Ram is cheap.

I have altered my old project to use an ECS library. This library operates like many other ECS systems. Simply with a registry of entities which you can assign components to with bit masks.

I'm trying to solve the problem of cycling focused elements to the front of render order now.  My current hang up is on whether or create a new registry or vector of pointers or if I should just modify the original registry of entities when I want to cycle the elements to the front.

Are there any disadvantaged to messing around with the original registry. I know it is kind of like having social security numbers and home addresses be the same thing, but I could possibly store another unique ID as a component to the entity.

50 minutes ago, maltman said:

want to make a GUI library which inflicts the least amount of mess upon the client codebase as possible.

Where are you drawing the line as "client codebase"? For example ones with their own markup style (e.g. HTML/JS especially with things like angular, XAML, Android XML, etc.) strive to move as much as possible into their declarative syntax and out of the imperative code/language so that the code mostly just handles events, data binding, and bits of dynamic creation/destruction of views, so you don't have a bunch of "new Button(50, 100, 'OK'))" type code.

 

50 minutes ago, maltman said:

I'm trying to solve the problem of cycling focused elements to the front of render order now.  My current hang up is on whether or create a new registry or vector of pointers or if I should just modify the original registry of entities when I want to cycle the elements to the front.

Not really sure without actually seeing your current structure as I never tried using an ECS specifically for GUI. Does one of your components have the ID for the parent and children?

I basically solved this by re-ordering the child references in a "root" UI node so that my child lists are always bottom to top and a tree-iteration can be naturally bottom to top or top to bottom. This doesn't allow me a CSS style "z-index" property however, if I want to add that I would need to track the z-index items separately, and having a separate render/z-order "collection" of all UI elements would certainly work.

 

A lot of what I did was based on experience writing various complex GUIs in HTML/CSS (with and without frameworks), wxWidgets, Android (find mostly under android.view and android.widget packages), and Java Swing, with some simpler stuff in Unity. Was there any GUI programming you are approaching this from?

 

As with all 3D rendering, using the depth buffer is also a possibility with the potential benefit of reducing overdraw, but doesn't work with alpha blending, any such elements still need back to front sorting. You also need to consider the need for mouse events to determine the top-most window at any given location, and possibly keyboard navigation to determine the tab-order.

 

This topic is closed to new replies.

Advertisement