Monday, October 18, 2010

LootGrab: HTML5 Game from Triangle Game Jam 2010

Spoilers in the Video! Consider Playing LootGrab first. (As of Sept 2010, Chrome was definitely the best option since Firefox and IE struggled, you can give your smart phone a try too).

2010 Triangle Game Jam game: LootGrab Video on youtube, or LootGrab Video on vimeo.

This year brought changes from the Game Jams of past:
First, I moved from the Research Triangle and now work at Google, and Adrienne came too (we've worked together on 5 of the game jam projects now). So, we got some fresh blood at Google to join us and ran a game jam in parallel with the 2010 Triangle Game Jam.

Second, instead of using C# we used HTML5 this year:

Third, you can Play LootGrab with a click of a button - ridiculously easy compared to all previous Jams where I didn't even bother giving you the gazillion prerequisites required.

Theme and Game Ideas
The theme this year was, "Placing Blocks". Here is my game concept, which didn't make the cut::

(someone pointed out it would be great from the side too, with ballistic arcs.)

We voted up ideas, and Adrienne's one out: LootGrab is about placing down blocks in a dungeon to influence the hero, instead of controlling the hero directly. The greedy guy runs for the closest loot, food, or exit ... without care for monsters or traps.

We figured we'd need a map editor, the runtime, and perhaps a level sharing system online via AppEngine. I was particularly attached to an idea of allowing user contributed game object definitions. Allow a user to upload an image and a snippit of javascript that defines it's behavior. How cool would a mod-able game jam game be? :) That was stretching a bit far though.


HTML5 is a grab bag of new functionality in browsers. Some of it is pretty cool (peer to peer networking, local storage, video and audio tags). We focused on two simple components, canvas 2d to draw and audio for sound effects.

In my day job I'm working to accelerate canvas with GPUs, as are others at Microsoft, Mozilla, and Apple. It's fairly fast even in software, and LootGrab runs fine without GPU acceleration. In fact, it runs on phones pretty well, such as my Nexus One Android phone. That's pretty cool, all we did to support mobile was to make sure we handled low frame rates without changing gameplay. To do that we used fixed time step gameplay logic (tick based), and just run as many ticks as needed to cover the amount of time elapsed.

Our use of canvas is basically clearing it, drawing a pile of sprites (via sub-rectangles of larger images), and also a line to show where the player is moving. Actually, we have a few layers of canvas stacked on top of each other. Theoretically we could have saved performance by not redrawing non animating tiles - just compositing them underneath.

Adrienne took on audio for sound effects, and did run into a bit of trouble. The sound effects were very short, and had to be padded out to longer audio lengths to trigger properly. Also, multiple instances needed to be created in case the sound was played more than once.


Several of us hadn't done anything substantial in Javascript before. Certainly not an object oriented game entity system that can factory from user created levels. Some complicated flurry of activity by Glen, Ian, Nat, and Gregg made that happen. The result could be cleaner, but worked well. We have JSON data blobs, e.g. for the tile definitions.

Things I loved:
Need to add extra data to your level components of game object definitons? Perhaps only to particular items? No problem! Just start typing. At runtime it is trivial to just check if the data is there and use it if so.

Writing some code and wish you could hang more data off an object? Just set that value! Check to see if it's === "undefined" later and you can pick up  your special data easily. Object definitions don't have to worry about implementation details of other systems, and those other systems don't need extra book keeping kept in parallel. e.g.:

  try {
    ctx.drawImage(this.img, ...);
  } catch(e) {
    if(this.error_printed === undefined) {
      tdl.log("problem with image " + this.entDefID);
      this.error_printed = true;

Development tools: Logging. Resource load timeline. Immediate mode editor: Hit a breakpoint, and just execute some code at the Javascript console.

Fast iteration time, though C# was great for that too.

Instant continuous "build"! Glen installed an Auto Reload Chrome extension and put the game up on a projector. Check in some code and see the game running it in 20 seconds. ;) Helps to have a game that can play its self.

Libraires such as TDL, and JQuery: some helper code for Javascript. It's not so important what you use, but you definitely want to not worry about the minutia.

Not so great?
I didn't use an IDE that had code analysis, and that's a very convenient feature of MSVC. Though, Ian had good things to say about WebStorm.

Also "classes" in javascript are syntactically very sad, and inheritance to my novice eyes looks messy. And variable "scoping" is dicey.

Debugging is functional and GUI, which is better than what most programmers use around my on linux. But it falls short of a modern debugger such as MSVC with C++ or C#.

Also, deciphering a web page via HTML, script, HTML embedded in script, CSS files, and dynamic changes to styles? ... yikes.

Things for Next Time

Would be nice to have some basics already written:
- Factory that will created entities from JSON data packs
- Cleaner audio solution
- Sprite system for canvas

Smaller teams. We had six on this project, and that's a bit much for a game jam game. Several were first time jammers, and several Javascript newbies, so it did really help to share know-how. But we wasted a lot of time getting started, coming to consensus on implementation choices, and stepping on each other's code.

The End

And now I leave you with some screen shots:

And a thanks to whoever oryx is, who created the sprites we used:


  1. This is interesting. Cool stuffs.

  2. variable scoping dicey? I gotta call you on that one. Variable scoping in JavaScript is one of its single most awesome features that makes it rock so hard!!!

    The reason is closures. In a language like C, if you have a callback you usually have a "void* userdata" field you pass in that will be passed to your callback so you can pass some info along. That sucks because you've got this ugly void pointer that you have to cast and pray you don't screw up.

    C++ and C# add objects, function objects or delegates but it still sucks. Instead of the callback taking a void* userdata now it takes some object you inherit from or you make a subclass and override the OnXXX function. It's a a buttload of boilerplate code where you have to go create a class and fill out a bunch of stuff just make it possible to access some data on your callback.

    JavaScript has none of these problems because of its scoping rules. JavaScript scopes by function, not by brackets so you never have to create an extra data holding class just for a callback.

    This is why node.js is so awesome. In every other language, writing an async based program (like a webserver or webpage) is a PITA because you have to create all these classes but in JS, because of the scoping, you get all this for free.

    Take a look at this example.

    Starting at line 122:

    First we're setting up a callback that says "when a http request comes in call this function". Next, line 130, it says "do an async fstat on some path and call another function when that's done". If it's a directory then on line 135 it gets an async directory listing and its callback takes the result and generates an HTML directory listing. If it was a file, line 158 reads the file async and its callback sends the file.

    Now, imagine all the work it would be to do that in C++ or C# or Java. You'd need all kinds of classes or a way to pass the request acquired at line 122 all the way down to the 2 final callbacks but in JavaScript, none of that is needed. It just works, safely.

  3. @gman:
    re: lootgrab post, just noticed you already had one:

    re: scoping:
    Closures are great. I don't know why scoping to functions instead of code blocks makes that easier, though.