Monday 16 November 2015

Honeycomb

So, this is a draft blog post I started back in October, but never really got around to finishing. Coupla reasons why, the first one being that eternally-damned beast known as motivation (well, a lack of it). The second is that I've just been, well, busy. Half of it has been me doing a lot of behind-the-scenes work, and half of it is just a rough month. Honeycomb! went into beta back around then, with some tweaks since. There haven't been many public updates (especially for such a small project) but I've been doing a lot of work behind the scenes.

I mean, all of this evolved out of me procedurally-generating hexagons and using maths (badly) to randomly-generate paths (badly, as it turns out). I was learning to build an actual graphics pipeline in Java, by overriding a single Swing class (JPanel; the general recommended use-case for painting graphics in Java). Before this, a lot of my work was interface / GUI-driven. It rapidly turned into something much more than that.

As with all of my projects, it had a purpose though. 2D graphics in Java. That was the aim, I achieved it, another tick box on the eternal To-Do List of Doom™ (patent pending). Very simple stuff, compared to most of the developers out there. As usual, I uncovered a bunch of faults with my implementation (fixed-step renderer is the biggest, but not the only one). In no particular order:


1. Fixed-step renderer. Basically, the main thread the game panel was running on I put to sleep every cycle for 1/60th of a second. This will give me somewhere just under 60FPS, dependent on the overall time cost of sixty cycles. Very simple, stops a simple 2D Java game from thrashing the CPU too much. Fraught with issues. What if 60FPS isn't ideal? What if the time cost exceeds, well, 0? This is why the game is a bit slower (though apparently people haven't noticed) on older CPU setups. Easy enough for me to notice even without a profiler as I switch between my 1.8GHz i5 laptop edition CPU at work, and my 3.4GHz i5 desktop edition CPU at home.

(for those so inclined, the work CPU is an i5-3337U and my home CPU is an i5-3570K. Benchmarks for the Benchmark God, etc)


2. I had no idea what I was doing. Okay, so this is probably a bit hyperbolic, but yeah. This started out as a technical test, it was never intended to be a game. When I got to things like score-tracking, and passing stuff around the core classes? It got a bit messy (though thankfully the initial structure was solid and I didn't have to rewrite too much for the extra logic). There are funny little bits here and there, like how the hexagons render on top of each other (in horizontal then vertical order) so you get weird overlapping when you Ctrl-drag a bunch of hexagons at once. I've got a quick fix for that (draw the hexagonal borders after the main hex shape; two separate loops as supposed to one) but in general, in future, this could be solved at a conceptual level.


3. Benchmarking. I'm not familiar with it. Certainly, I'm adept at writing decent Swing code that doesn't have a hissy fit whenever you open a file dialog, and I'm good at cold start vs. start when it comes to launching Java applications from the platform (a small splash screen goes a long way). But this is something a lot lower down than the whole Swing stack, and it requires a whole lot of System.currentTimeMillis() to manage effectively. I probably need to subclass that in future and keep a constant track of various different time states. As it was, when I had to implement pause functionality I nearly stabbed my screen trying to work out interactions with the main game thread, the overall GUI implementation and how to update that codewise and visually.


Not that this helped at all, and there really isn't much of a nicer way to do it. Well, apart from write your own class / utility to handle timestamp rendering from the ground-up.
 

4. Redundant code. Like, lots of redundant code. My game is (sensibly) controlled by states, and for each of these states the renderer does something different (again, sensibly). The problem is when I copypaste the same twenty to a hundred lines of code for each game state (of which there ended up being several). This is the problem with rapidly-iterating on a concept without the benefit of a code review, or similar. Teamwork would expose this relatively quickly, but working by myself I always manage to catch myself a bit too late.

The same goes for the GUI logic. I'm very used to Java's Swing library and all it entails, I'm pretty proficient with it. Problem is you have access to none of that when you're painting everything manually in Graphics2D. A lot of pixel-perfect drawRect() calls, for example.

Which leads me nicely onto what went right with the project, and why I'm now working on something separate (but not completely separate) that for the sake of having to have human-readable project names I've called PlayerDemo. This is another experiment to work on a system / library that handles Shape-based GUI objects in the context of Graphics2D. You add your visual assets, you define a Shape for them to inhabit, and the whole process is made quite easy.

This was after my success in making a graphical font system that takes in a spritemap / bitmap of your font characters and maps them to an a - z / A - Z / 0 - 9 / non-alphanumerics sequence (that can be programmatically-defined). This allowed me to easily use a custom font throughout Honeycomb! that is far better than anything I could've made in the conventional TrueType format. At the moment it favours monospaced font creations, but I can extend that quite easily. The hard work is done, really.

So, a lot of progress. More updates for Honeycomb! in the future, especially when I get these Shape-driven classes fixed up. Will make building a more involved UI easier going forwards, adding in game options, etc.

Watch this space!