I should probably apologize for the previous entries being so sparse on content. Truth be told writing about weekly work activity has an element of embarrassment to it. Bothering to write something and to put it out there implies that you’re supposed to have a sense of authority on the topic. But I don’t have that authority on any one topic, so it’s inevitable that anything I do is going to not be the best way to do something.
Game development does such a good job at not covering most of its topics very well that I feel pretty ok talking about my dumb methods there, but UI systems are a rather better covered topic so I felt a little worse about talking about it. Clearly this thing is going to take way too long for me to not write any blog entries of substance during it. So here’s some dumb stuff I had to work on this week:
1. Layouting is terrible.
Since I wanted to do this as quickly as possible I settled on having layouts determined by static sizes and then having the children simply adapt to the layout they were given (rather than having the layouter adapt to its children’s sizes). Ain’t writing something for everyone here, for the most part this is good enough for my needs since I can make sure the children fit in the given area. It allowed me to avoid questions like “what’s the size of something that is supposed to stretch to fit its designated area when it doesn’t know what its designated area is yet”.
Then I ran into a snag when I realized I wanted line wrapped layouts (primarily to handle text mixed with images). That had to adapt to its children’s sizes. So I pretty much just scoped out how other UI systems handle it and came upon the concept of minimal sizes. A button might be set to stretch to fill the total available area, but you can also figure out the minimum size required to fit that button based upon the width of the label of the button. By finding the largest minimum size of all your children you can find a pleasing standard area to give to each of them. Good enough. Will even save me some time when I have to build this stuff later.
2. Drawing text is terrible.
Text in computing is one of those things that seems like one of the simplest things ever, but actually isn’t really. Drawing in a game becomes a fairly taxing operation because each character is drawn separately. So 500 characters worth of words is effectively similar to drawing 500 sprites to the screen. You can reduce some of the costs of this by using bitmap fonts and batching their drawing. You can also just pre-render all text into memory once and then just draw that- which is the primary technique I’ll be using since it’s fairly easy to integrate into the current system.
But the big thorn in my side is laying them out. Line wrapping a clump of text is fairly easy: check remaining width for each word, add newlines as appropriate. Line wrapping a clump of text that’s mixed with other UI nodes is a bit more painful. The simplest technique is to just make each word its own node and freely mixing them with all the other UI nodes.
But that has some terrifying implications: do you really want to perform collision checking on 500 words every time the user clicks on somewhere with a bunch of words (even if they don’t check for collisions they still have to get iterated through)? You want to waste memory on storing the position, scaling, etc on 500 words? You want to create 500 sprite objects for them? You want to perform 500 update operations on them any time their parent changes their inherited properties a bit?
Given I was considering implementing the scene graph management in Python to save time I’m extra worried about performance troubles related to doing it this way. But maybe I’m just worrying about nothing. I performed a basic performance test and the game didn’t hit a performance snag until I started drawing around 3500 4 letter text sprites, for instance. I have a relatively high end computer (which makes trying to estimate performance with it scary), but it’s unlikely we’ll go past maybe 500 words on screen at once, and caching will offset the rendering overhead, and it’s unlikely the management of that many nodes is actually that big of a deal. I’m probably just prematurely optimizing. In fact now that I look at what I just wrote I’m pretty confident that I am. If it actually becomes a problem, adding hackier optimizations won’t be much harder to do later than they would be now.
3. UI libraries are terrible?
This marks a month of planning the UI system. There’s so much I could have done for making a better game in that month. It will almost certainly take another month (maybe two!) to actually build this thing. Planning isn’t even entirely complete, I need to make one or two more passes over it. Needless to say I started to panic about the time investment (especially considering that at least 6 months total have been spent on the current UI library and experimenting with other libraries). So I started doing some research one last time on other libraries.
Probably the technique that stood out most in my desperation this time was embedding a browser into the game (via a wrapper such as Awesomium or CEF). My original research years ago I dismissed the idea pretty quickly just because throwing part of a browser into an application just seemed like ridiculous overkill. It still does, but after looking at other people’s implementations of such things I started to see the benefits. You can throw in existing implementations of complicated UI systems with relative ease. A browser will almost certainly do circles around you when it comes to doing extremely dense UI.
Some of the other benefits like “most people are familiar with HTML/CSS/JS” are a little lost on me since I don’t really do webdev, which is a big part of why I dismissed it originally (I honestly couldn’t even conceive of web skinning being good enough for game UI, but research shows that it’s pretty capable). If I had to start this project over again I might go with this solution now. But right now I’m just tired of running into critical problems with other people’s work. I don’t ever want to have to switch the fundamentals of our UI system again. Using another library will always have a risk of investing into it and then finding a show stopping problem. I can’t take that risk this late in the game. I might run into problems with my own stuff, but I can always fix it.