So, we have added progress on the Nucleus project today. The big news is it’s moved along enough that I can post some footage of the game in play, because all of the basic play elements necessary to that end are done. There are still some major bugs floating around, and a few crop up in the video, but on the whole it’s coming along nicely.
I also had cause to do some major performance evaluation and tuning a day or two ago, so I’ll be talking a bit about that today as well, since I tripped over a rather common issue that I expect will be cropping up in a lot of WP7 games, as well as discovering a major DON’T that was obvious in hindsight.
So, first things first. The video. This is taken from the Windows version of the game (oh, I mentioned there’s a Windows version now, didn’t I?) due to not being able to get a decent framerate out of Fraps with the Emulator. No surprise there. Also no surprise: it’s unfinished and rough. Yhere are glitches in the code, and my particle system isn’t finished yet, so some of the visual feedback for scoring and such isn’t currently present. But that’s why we call it pre-alpha or whatever.
Windows Live Writer is an amazing blogging tool. It understands WordPress and other software besides Live really well (to the extent of allowing you to edit in your blog’s theme, even), has all sorts of nice features, and I’ve just discovered it can log in to a YouTube account and give you a clean little list of all your videos to pick from for embedding. Wow.
It’s worth noting that the claims that XNA makes for relatively painless cross-platform is true. Very few changes had to be made to produce the Windows version of Nucleus. I wrote a new input handler class to work with the mouse, and used a nice clean #if WINDOWS / #if WINDOWS_PHONE compiler directive to choose which version of the class is used. The rest of the code just plain works. It’s fantastic, and a very good thing, because most of my testers won’t have the WP7 emulator, and they CERTAINLY wont’ have a device.
Benchmark Early, Benchmark Often
As I’ve spoken about before, twice in fact, taking the time to keep track of the performance you’re seeing on a regular basis makes it much easier to keep performance bottlenecks from piling up as you write code. I’m still not saying that you should optimize constantly, because that’s nitty-gritty detail work that you’ll probably end up re-doing, but keeping an eye on the big performance picture with something as basic as a frame rate counter is useful, allowing you to catch big problems before they get buried under other code.
I noticed that Nucleus was hitching up now and again recently, so I did a fresh build on the Windows version so I could hit the profiler and have a look. this isn’t as useful as VS 2010 Pro’s Analysis tools, but the Garbage Collector is the most common cause of weird stuttery hitches. And indeed, when i ran the profiler, I found a mess—3 collections and a huge pile of garbage.
Specifically, I found a large set of exception and string objects piled up on the heap. The first was a stupid bit of coding on my part—rather than write my code better, I was handling an IndexOutOfRange exception with a try/catch pair. This was a bad idea, though, since it was actually easy to remove the exception throws with a little thought(and made the code run a little faster by avoiding a function call in cases where the exception would have thrown).
The other issue—the strings—is more forgivable, and much more likely to crop up in people’s games. It’s also relatively easy to take care of. the problem is that strings are objects in C#, but look like a native value-type. it’s easy to use a lot of string literals and such in a Draw() or Update() loop, and generate a lot of strings that end up in the garbage list.
Fortunately, most XNA functions that take strings are overloaded to also take StringBuilder objects. So the fast solution is to break strings and string literals out of the loops, and store them on StringBuilders. If you specify a capacity up front that has all the room you expect to need, you can build and rebuild the strings on the StringBuilder’s internal array without any re-allocations. And without any garbage. The biggest offender I had in my code was the Score string, which was being built on every frame with something like:
“Score: “ + score.ToString();
BAD IDEA HONEY. (I like his hat, tho…) this is a naive way to handle it that generates a lot more strings objects than it looks—all of them temporary and all of them on the heap. Set into a draw loop that runs 30 times a second… well. It piles up really fast.
Fixing the issue to use StringBuilder objects outside of the loops that simply get manipulated inside them produced a much better situation. The heap size isn’t quite leveling off to a stable situation still, but running the game for over ten minutes without a collection is a good situation for Nucleus. I can live with a GC if it only happens ever 10+ minutes on a game that I only expect to see being played in five minute sessions or thereabouts.
The point here, is you’ve got to keep track of this, because performance issues like the string garbage may not be as noticeable once the game’s got a thousand particles and who knows what else going on. It’s best, in my opinion, to keep a set of milestones for yourself and do a cursory check after each one.