Tuesday, March 9, 2010

The Tortoise and the Hair

I've given in. I have succumbed. I have turned up my tootsies to the sky and cried 'Uncle!'

(It's always perplexed me that one's indication of submission would be to scream out the title of a family member. Odd. Possibly Freudian?

I have given up writing the Android version of Flaboo! The reasons? Oh, there are many...

1) Speed.
2) Java constraints.
3) The difficulties of writing native code.
4) The un-enjoyable development environment...

...to name but four off the top of my increasingly shiny head. My remaining hair now has a tuftier appearance than usual due to my grabbing fists-full of it in frustration.

When I left Lionhead, I filled my mind with thoughts of skipping through flower-filled fields, care-free, bouncing along greeting the trees and bunnies with a latte in one hand and a tiny, portable dev-machine in the other. In these fantasies I'd sit under a tree while butterflies kindly offered me refills (strong buggers, these particular lepidopterae) and helped me with my typing.

As such, Flaboo! was largely an experiment. It was an experiment in testing out the iPhone development environment. It was an experiment in working entirely by myself. It was an experiment in changing my life.

As such, it was pretty successful. I love Flaboo! I really do. It's one of the purest and best games I've ever written. It might be simple, but it's really, really 'clean' and incredibly addictive.

As such, I thought that branching out and spreading the Flaboo! love to other platforms would be a good thing to do. After all, the game originally started out as 'Hopping Mad Simon', written in Java back in the days of the Siemens MC65. How hard could it be?

Earlier in the year I went to the Google Android developer conference in London, and the folk at Google were kind enough to give me a Nexus One. They also listened to my questions, and have a spiffing support site filled with little gems for the burgeoning android developer community to sink their little milk-teeth into.

How jolly!

Creeping Dread
In horror films (um... take 'The Shining' as an example) numerous small details of 'wrong' slowly congeal into one super-powered 'mighty-wrong'; an axe-wielding Jack Nicholson in the case of 'The Shining', and Wendy Richards in my own personal nightmares.

Look at that face. It's Evil Edna in a wig made from Dougal's carcass. Brrr.

(Youngsters should look up Will-o-the-wisp and Magic Roundabout)

In the case of the Android, the first 'wrong' came when using the device. Its battery has roughly the lifespan of a chain-smoking mayfly who's eaten polonium-sushi for breakfast, followed by a nice long sit on public transport while wearing a bushy beard and a bulky, ticking backpack. Not long at all.

However, in light of the lovely pixel resolution and the early promise of a big friendly API allowing me to do things like play multiple sounds with minimal effort (no openAL! Woohoo!), it seemed like we were going to be bestest friends forever.

Then I started coding for it.

Android uses Java as its primary development language. Java?! It's a language named after coffee! That's awesome! Coffee! Yaaaay!

A warning bell should have gone off when everyone writing Android apps said: "Don't allocate an object. Ever. No, really." At the time I pulled a confused 'Father Dougal' face and moved on. It was a hint of the 'wrong' to come.

Converting projects isn't really a lot of fun as it mostly consists of copying and pasting code over to the new environment, watching a load of red lines appear, and realising that your original code was a bit poo. Slowly, day by day, you watch more and more lines (out of your 300 000 total) turn from red into black.

And then break. You see, Java is a bit 'special'... seemingly just for the sake of it.

For a start there are no unsigned types nor typesafe enums. More little bits of 'wrong'.

(Jack is now sitting at a bar drinking imaginary scotch. It's only a matter of time before the creepy eyebrowless twins make an appearance.)

Also, Java's strings and arrays do not terminate with null characters. That broke a whole bunch of my data-parsing code without me realising it. It wasted about a day; a day where I both gnashed my teeth and smacked my head on the table such that I now have bite-marks in the woodwork. More tiny helpings of 'wrong'.

(Oh no! Jack's typing 'All work and no play...'!)

iPhone Flaboo! uses openGL to maximise performance. I used big interleaved vertex buffers and manipulated them in real-time to get the rotations and scaling you see in the game. Java does things a bit differently (uh oh). You have to use ByteBuffers instead of arrays, and there's no sensible way to cast different portions of them to different types - vital if you're interleaving colours and vertex information. I wouldn't mind if ByteBuffer manipulations were fast, but they really, really aren't. Another 'wrong'.

The Slow Dance
Despite the various hiccups and a nagging feeling that this wasn't as much fun as it should be, I got all my lovely, complex, multi-layered, articulated sprite code working today. I tapped the screen a couple of times causing Fat Chick's cheery little face to appear on the screen.

By the time the game had 20 sprites on-screen it had slowed to a crawl. If you wiggled the roller-ball on the phone it ran at half of even that glacial framerate. In response I looked around once more to see if there was anything I should be doing (or not doing) that would help speed things up a bit.

After a search, the main advice seemed to be to change all my maths into fixed point. Fixed point? I've not used that since 1995! How retro does your hardware have to be to rely on the veritable 'Stonehenge' of game engineering? Big 'wrong'.

(Jack has now seized the axe and is telling all present that 'Johnny' is in the building.)

The other advice was to rewrite a majority of my code in C and compile it, along with all the android OS in a Ubuntu virtual machine or Cygwin. Yuck. As of two days ago there's better support for openGL in the NDK, but still.

Take a look at this. It's the final, capitalised Wrong to end all 'wrong-kind'. Debugging native (i.e. workably fast code) makes Lost's plot look straightforward.

(We've now seen the man in the bear suit, Jack has chased Danny around the maze, Halloran is dead and the titles have begun their roll. Oh, and Wendy screamed and cried a lot, looking much like a novelty bottle-opener with eyes.)

The Final Straw
I started Fluttermind to have fun and experiment with gameplay and graphics, not sit staring at a screen of hex for a day trying to figure out why a @&%£ing cloud isn't rendering properly.

There's no pithy afterward, I'm afraid. Eclipse/Google/Android and I part company today. It's the supermodel that turns out to be a hose-beast. It's the British Mars-probe that crashes due to a measurement error. It's the 'Avatar' that turns out to be 'Dances-with-Smurfs'.

Farewell Android. As I wander off into the distance I have absolutely no fear that you might follow me. Even at my pace you have no hope of catching up.


  1. boo hoo
    no flaboo
    for my trusty hero

  2. If I feel particularly spiritually dirty and guilty in the future, I might pick it up again in much the same way a flagellant does a birch twig.

  3. How many lines of code?!!? :)

  4. "copying and pasting code over to the new environment"

    If you've seen the "Coding horror" icon from the 'Code Complete' books, that is what should be next to this sentence

  5. You should see some of the code I discovered during the process. :-)

  6. You should give WebOS some love. It seems a lot games have been ported already and it has only taken 2-3 weeks to do it.

  7. Just bought Flaboo! for the kids to play on my phone... looks nice.

    But as a long, long time java geek I gotta say.... Is you crazy?!

    JAI, java3d and some of the java 5 gathering/scatter bytebuf would do what you're looking for above. But let's face it. Porting C to java line by line is pretty much doomed to failure, at best. Dunno how much of this stuff is available on the android sdk anyway... I'm working on iphone apps!

  8. Note that I'm only really posting generalisations rather than well-documented examples of my porting process: this is not a technical blog. I think a few people may have taken it too literally.

    I can point to two areas that illustrate why I find the general practice useful, though.

    1) OpenGL order of operations. The order is critical, so reminders of the order of calls is vital. No point in going back to a blank screen and rediscovering all of the matrix-altering operation quirks one took weeks to solve the first time around.

    2) Higher level constructs such as my gestalt entity code. If I'd just 'rewritten it from scratch', I'd have missed out half the weird and wonderful stuff I did in order to get multiple, hinged rotations to work.

    Since my code is both heavily commented, and self-commenting (I've read enough books to know what I'm doing), there's a hell of a lot of information I'd rather not simply re-invent.

    If I'd rewritten my objective-C as pseudo-code and then reimplemented it I'm sure nobody would have commented. :-)

    Having fairly extensive first-hand knowledge of writing for Android I feel fairly well qualified to say that there's a good reason there are few fast, arcadey games on the platform, and it's not developer ineptitude. Try writing native code for yourself.

    I'll have a beer waiting for you after you've done crying and batting your head against the screen.