Palagpat Coding

Fun with JavaScript, HTML5 game design, and the occasional outbreak of seriousness

Time Flies Like an Arrow...

Thursday, September 5, 2013

"... and fruit flies like a banana." Yeah, linguistics humor. Sorry.

Anyway, the truth behind the joke remains — my summer has breezed right past me, and I find myself standing on the precipice of autumn (whatever that means in northern California). To say I'd hoped to have done more here on the blog this summer is, probably, stating the obvious.

The Reason

Things at work have been as crazy as you'd expect life at a startup to be, but all in very positive ways. I feel like we're building something really cool, and I keep waiting for the green light to actually talk about why. I suspect it'll come soon. Meanwhile, we're hammering away at the mountain of backlog tasks in Jira, and it's such interesting work, I even find myself doing it in my down time at home: precisely when I used to work on Tangle, MUGEN, and my other side-projects. I kind of knew this was a possibility when I left my last job, which was secure but a little technically unexciting. My side projects were always there to give me something intellectually stimulating to work on when I wasn't always able to scratch that itch in the office. Now that I'm on a team and in a role where that itch is being constantly scratched, well, I guess you could say it's harder to task-switch.

So......?

So the big question at hand, then, is what I'm going to do about it. I do have a lot of things on my back burner, and am trying to mentally clear a place to work on some of them. I'd like to get back on the "One Game a Month" horse. I'd like to get back to work on my outstanding MUGEN projects. I would LOVE to get my groove back on Fenjin. In the interest of taking baby steps, and with the idea that incremental progress is better than no progress at all, I've updated my Games page, including links to several of the in-progress things I'm playing with.

Some of which I haven't blogged about yet — stay tuned.

One Game Down

Monday, June 3, 2013

So, that was May.

I actually shipped something, even though I didn't get everything done I would've liked. Actually, I spent all my spare cycles last week trying to get touch events added to Tangle's InputManager, so Quilt would have mobile support, but I ran out of time without getting it fully functional. Sigh.

That also meant I didn't get many more levels added to Quilt (it currently sports 8, and the difficulty curve is a little too exponential... level 7 should be around 10 or so, and level 8 shouldn't come in until 15 or 20), nor did it get the level-reset command I wanted to add, even though that would have been like 10 minutes of work. At most.

I may still sneak that one in, in fact.

The Importance of Moving On

I read Jeff Atwood's blog, Coding Horror, pretty regularly, and even though I don't always agree with everything he has to say, this post left a pretty strong impact on the way I approach software development:

At the end of the development cycle, you end up with software that is a pale shadow of the shining, glorious monument to software engineering that you envisioned when you started.

It's tempting, at this point, to throw in the towel -- to add more time to the schedule so you can get it right before shipping your software. Because, after all, real developers ship.

I'm here to tell you that this is a mistake.

Yes, you did a ton of things wrong on this project. But you also did a ton of things wrong that you don't know about yet. And there's no other way to find out what those things are until you ship this version and get it in front of users and customers.

So, yeah. Quilt's out in the wild now, and if anyone wants to submit feedback on Github, Twitter, or G+, I'll accept it.

I'll also keep tinkering, because that's what I do. But it's time now to turn my attention to June's game for #1GAM.

More on that soon.

Quilt, the De-Tangler

Monday, May 27, 2013

Quilt and TangleJS symbols intertwined I'd said I was going to post something new here every Friday night. Then I went out of town on a house-hunting trip, and things got kind of busy. So... I missed blogging this past Friday, but what I did do, is get Quilt to the Minimum Viable Product stage, meaning it's online and you can play it, even if all the mechanics have yet to be ironed out.

Play Quilt, my #1GAM entry for May

The cool thing about working on something new that's not a blog post or Tangle tutorial is that my Tangle game library actually grew as a result of something I needed for this project, instead of me having to deliberately grow it by design. That was kind of a "duh" moment for me, actually: for the past year or so, my blogging here has gotten a little wrapped around the axle of the Let's Make a Canvas Library car, so to speak, and I didn't have the spare attention to work on anything else, or really to even work on Tangle as much as I would like. Now, in the past 2 weeks, I've put together a game (albeit a simple one), and Tangle has organically grown in the process! So, yeah. I should've been doing it this way all along, methinks.

I'm not finished with Quilt yet; there are a few more features I think it really needs to be "done" (mobile support, more levels, and level-reset command top the list), but to honor the spirit of One Game a Month, when May's over, I'm going to stop tinkering with it and move on to the next game on my backlog. :)

In my next post, I'll try to talk in more detail about Quilt, and the changes it prompted me to make in Tangle.

What is Quilt?

Thursday, May 16, 2013

Quilt logo

Last week, I mentioned that I had joined the One Game a Month challenge. For my May game, I decided to dust off the not-quite-finished Blak & Bloo (I still need to write up that whole experience and why it ultimately failed), and reuse some of its code with the latest improvements I've made to my in-progress Tangle game engine. The idea I came up with is to eliminate physics (which turned out to be more of a pain than I expected), and make a simple pattern-matching game.

I'm calling it "Quilt", and you can follow my progress here. Next week, I plan to blog in more detail about my approach, and how I'm going to avoid the Blak & Bloo problem.

Reboot

Friday, May 10, 2013

I'd forgotten how busy working from home can be. That, coupled with the fact that the stuff I'm working on is pretty fun (more on that soon, I promise!), means that recreational coding has taken a back seat these past few months. :(

It doesn't help that my next entry in the "Let's Make a Canvas Library" (LMACL) series is way too much to cram into a single post, and I've been having a lot of writer's block over how to break it up.

Sooo.... here's what I'm doing about it: I joined the One Game a Month challenge. Like its founder, Christer Kaitila (aka @McFunkypants), says on the site: "Starting a game is easy. Finishing a game is hard." Hopefully this very visible commitment will give me the swift kick in the rear that I've been needing. I've got a couple of half-finished games on my back burner, plus a simpler one that I think might help me detangle (heh) the code roadblock I've hit with LMACL and TangleJS. I also plan to blog at least once a week, on SaturdaysMondays, to report on progress. Otherwise, it's back to making games and not so much on the talking about making them. :)

Wish me luck.

Let's Make a Canvas Library: States

Tuesday, January 15, 2013

Welcome to another edition of Let's Make a Canvas Library, our funkalicious tutorial series all about the basics of building a personal code library for making neat stuff with HTML5 Canvas.

(As always, the aim of this series is primarily educational, not to build an amazing library that everyone and their dog is going to want to use. But for anyone interested in following along, or anyone who likes the API we're building and wants to use it, it's available for forking and download on my Github account as TangleJS.)

Today we're going to talk about state machines.

State of the State

One of the most useful things I learned in my first semester of college computer science (back when dinosaurs roamed the earth and CS 121 used Pascal as its introductory language) was the concept of the Finite-state machine (FSM for short). To oversimplify, an FSM is a set of program states and a set of transition rules for moving between them. A state machine can only be in one state at a time, and its current state determines how it reacts to any inputs it receives.

As a simple example, the Wikipedia entry for FSMs describes a state machine that models the operation of the humble turnstile, like you might see at a state fair. A turnstile has two basic states, locked and unlocked, and two rules for transitioning between them: a coin will move the turnstile from its locked state to its unlocked state, and pushing the arm to pass through the turnstile moves it from the unlocked state back to locked again. Note that putting more coins into a turnstile that is already unlocked simply keeps it unlocked, and the act of pushing on a locked turnstile similarly doesn't cause it to change its state (i.e. it stays locked):

Transition diagram for a finite state machine modeling a turnstile

Simple state diagram for a turnstile. Original image by Chetvorno, retrieved from Wikimedia Commons

Another common way to visualize a state machine is via a state transition table, like so:

Initial StateInputs
coinpush
LockedUnlocked-
Unlocked-Locked
State transition table for a simple turnstile

In this kind of table, every state is listed, along with every possible input, and for each combination of current state and input that cause a state transition, the new state is listed. So far so good?

State of Play

“So,” I hear you asking, “What does this have to do with games or canvas...?”

Quite a lot, it turns out. First, your game itself will likely have a state machine (with states like loading, title screen, options menu, playing, paused, and game over):

Initial StateInputs
Enter key"assets loaded" event"Player die" event
Loading-Title-
TitlePlaying--
PlayingPaused-Game Over
PausedPlaying--
Game OverTitle--
State transition table for basic game logic

In this hypothetical game logic, the Loading state doesn't handle any input at all, the Title state allows you to press Enter and start playing the game, and that same key input allows you to switch back and forth between the Playing and Paused states. The Playing state also handles a "player died" event, which causes the game to go to a Game Over state.

Likewise, your game will probably have entities (more on this in a future article) such as a player, obstacles, and enemies, and each of these may have its own state machine. Take, for example, this overly-simplified (and abbreviated) state machine for the main character in a side-scrolling platformer game (think Super Mario):

Initial StateInputs
Up arrowRight arrow"state end" event"touch enemy" event
IdleJump upRun forward-Die
Jump Up-Jump ForwardFallDie
Jump Forward--Fall ForwardDie
FallDouble JumpFall ForwardIdleSquash enemy
Fall ForwardDouble Jump-IdleSquash enemy
Run ForwardJump Forward-IdleDie
Die--Trigger "player die" event-
State transition table for a platformer hero

You may have noticed that I've been including a couple of different kinds of inputs in these state transition tables: direct user inputs, such as the player pressing a key on the keyboard, and other, more abstract events such as "player die" and "state end".

At this point in the article, I originally went on a several-paragraph tangent about how complicated input and event handling are, when I realized that the subject is complex enough it deserves its own blog post. So for now, let's just assume our state machines have a fixed set of inputs (some user-driven, others event-driven), and we'll save the actual capture of those inputs for another day.

State of Control

It's pretty simple to start using the "state machine" paradigm in your JavaScript code. A naive, ad-hoc approach might look something like this:

  var state = 0;
  var assets = new AssetCache(),
      canvas = document.querySelector('canvas'),
      context = canvas.getContext('2d');

  /* (specify assets to load) */

  function _update() {
    switch(state) {
      case 0: // loading
        if (assets.ready()) changeState(1);
        break;

      case 1: // title
        // (check user inputs and change state accordingly)
        break;

      /* logic for the rest of the states */

      default:
        break;
    }
  }

  function _render() {
    switch(state) {
      case 0: // loading
        context.fillStyle = "#ffffff";
        context.fillText('Loading...', 100, 175);
        break;

      case 1: // title
        var imgSplash = assets.getAsset('title');
        if (imgSplash) {
          context.drawImage(imgSplash, 0,0, 640,480);
        }
        break;

      /* rendering code for other states */

      default:
        break;
    }
  }

  function changeState(stateNum) {
    console.log( "Entered state", stateNum );
    state = stateNum;
  }

  // set up main loops
  Tangle.init(_update, _render);
  Tangle.play();

This works fine, and I built my first couple of game projects using this approach. It just starts to feel a little copy/paste-y after a while, and any process involving copy/paste of big chunks of code is going to be susceptible to error.

It also violates the DRY principle, because you've got multiple switch statements (one for update() and one for render()), and are spreading the logic for your states across (at least) those two functions.

If we were to abstract this into a reusable StateManager class, it might look something like this:

  var assets = new AssetCache(),
      states = new StateManager(),
      canvas = document.querySelector('canvas'),
      context = canvas.getContext('2d');

  /* (specify assets to load) */

  states.addState({
    id: 0, title: 'Loading',
    tick: function(me) {
      // If all assets are loaded, return 1 to tell 
      // StateManager to change to state 1. No change otherwise.
      return (me.assets.ready()) ? 1 : null;
    },
    render: function(me, ctx) {
      ctx.fillStyle = "#ffffff";
      ctx.fillText('Loading...', 100, 175);
    }
  }); // end of state 0
  
  states.addState({
    id: 1, title: 'Title',
    tick: function(me) { /* update any title entities */ },
    render: function(me, ctx) {
      var imgSplash = me.assets.getAsset('title');
      if (imgSplash) {
        ctx.drawImage(imgSplash, 0,0, 640,480);
      }
    }
  }); // end of state 1

  // (other states omitted)

  // StateManager event callbacks
  function stateChange(data) {
    console.log( "Entered state", data.id, ":", data.title );
  }
  states.events.changeState.watch(stateChange);

  
  // set up main loops
  function _update() { states.tick(this); }
  function _render() { states.render(this, _context); }
  Tangle.init(_update, _render);
  Tangle.play();

So now each state's logic is in a single place, and if you wanted to, you could even externalize each state definition into its own .js file, if they get big enough to be unwieldy. In most cases like this, the game's update() and render() loops just defer to the StateManager's tick() and render() methods, making the main Game.js code a lot more streamlined.

Another approach might be to make this a functional mixin instead of a creatable class, if you prefer that coding style. Either way, the point is to encapsulate your state logic and make states a little more self-contained.

State of the Art*

Finally, let's look at the demo! As in the previous articles in the series, I've created a simple demo that leverages Statemanager in conjunction with the parts we've already built (AssetCache and Tangle Core) to show how they can be used to start fleshing out some actual game logic.

(Maybe next time we'll have enough building blocks in place to actually make something interesting!)

As was the case with the last few components we've built, StateManager can be used in a variety of applications, canvas or no canvas, and should work in pretty much any browser that's not a pre-version-8 Internet Explorer.

Next time we'll take more time talking about how to capture and deal with user inputs and game events. See you then!

Resources

Let's Make a Canvas Library: Game Loop

Sunday, January 6, 2013

And... hiatus is over! Sorry for the delay, it's been a crazy Autumn.

Welcome back to Let's Make a Canvas Library, our tutorial series focused on the principles of building a good personal code library for writing games and other cool stuff with HTML5 Canvas. As a reminder, last time I said we'd cover:

“the basics of building a game loop, and actually use AssetCache to build something interesting.”

So, let's talk about game loops.

(remember, my goal with this series is mainly educational, not to build the jQuery of game libraries. But for anyone interested in following along and messing with what we build, you can grab the TangleJS repo on Github)

Anatomy of a Game Loop

A basic game loop

A basic game loop. Image by LazyEyeOzzy, retrieved from Wikimedia Commons

Almost every type game you'll ever play has some form of game loop at its core; most will look quite similar to the above diagram. After any necessary startup (loading resources and such), the game loop takes care of a few basic things:

  1. Handle player input
  2. Update the state of the game and any objects
  3. Render the game state back to the user

Each of these steps break down into sub-steps: handling player input implies collecting it and processing it. Updating game state may include things like running enemy AIs, calculating new positions for moving elements, and collision detection. And user output may include graphics, sounds, or even haptic feedback (like vibration).

The traditional way to achieve these types of loops in JavaScript is via the setInterval method:

Game.fps = 50;

Game.run = function() {
  Game.update();
  Game.draw();
};

// Start the game loop
Game._intervalId = setInterval(Game.run, 1000 / Game.fps);

(Example taken from nokarma.org)

This works pretty well, and many people have used it to great effect. Recently, though, a few of the browser makers have decided to give us a new and improved API called requestAnimationFrame (You can tell it's still pretty new, because the browser vendors still have their own proprietary-named versions: mozRequestAnimationFrame, webkitRequestAnimationFrame, etc). Its purpose is to streamline the management of in-browser animations, since the browser can internally optimize things like repaint cycles that aren't visible from the JavaScript layer. Plus, if your browser tab is hidden, the browser can effectively pause your animation for you, leading to better performance, memory usage, and so on.

Sounds good, right? Here's how we might change our loop to use this new API instead of setInterval:

Game.run = function() {
  Game.update();
  Game.draw();
};

function animate() {
  Game.run();
  requestAnimationFrame( animate );
}

// Start the game loop
animate();

I have a couple of thoughts when I see a game loop like this. One is that it violates the DRY principle, because you have to have this weird animate() function, whose sole purpose is to recursively call itself via requestAnimationFrame, then call Game.run(). Ick.

The second thing I notice is that there's no way to specify frames per second anymore, like there was in the first example. The main reason for this is that requestAnimationFrame is optimized by the browser, so the rate at which it gets called depends on processor speed, the amount of system resources available, and whether the tab is active. For the rendering portion of our game loop, that's just fine — there's no point in burning up your processor drawing to a tab that's not even visible. But what about the update portion of the loop? Should it be bound to the speed at which things are being drawn to the screen? Logic (and indie game developer Chandler Prall) would say no. So, let's try again:

Game.fps = 50;

function render() {
  Game.draw();
  requestAnimationFrame( render );
}

// Start the game's render & update loops
render();
Game._intervalId = setInterval(Game.update, 1000 / Game.fps);

That seems better. Now the draw code can leverage the browser's optimization path via requestAnimationFrame, and the update code can run at our specified rate. If you wanted to get a little more advanced, you could probably try and offload your update loop to a Web Worker, but that's well beyond the scope of this article.

There are a couple of things that this last version of the code doesn't address, which I often find myself wanting in my JavaScript game projects: the ability to dynamically show a realtime FPS count, and the ability to pause and resume the game. Both can be pretty easily accommodated with a simple helper class to abstract this basic level of game management, so I've added one to Tangle, which I'm calling Tangle Core. Here's the meaty bit (the real thing is wrapped in an AMD module, but this gives you the gist):

var tangleCore = {
  init: function game_init(updateFunc, renderFunc, fpsFunc) {
    _paused = true;

    if (fpsFunc) {
      if (typeof fpsFunc == "function") {
        _fpsCallback = fpsFunc;
      } else {
        console.error("[Tangle.main] >> Provided argument fpsFunc is not a function.");
      }
    }

    if (updateFunc && typeof updateFunc == "function") {
      _updateLoop = updateFunc;
    } else {
      _updateLoop = null;
      console.error("[Tangle.main] >> Provided argument updateFunc is null or not a function.");
    }

    if (renderFunc && typeof renderFunc == "function") {
      _renderLoop = function() {
        _checkFPS();  // calls fpsFunc with the latest calculated FPS
        renderFunc();
        _renderLoopId = requestAnimationFrame(arguments.callee);
      }
    } else {
      _renderLoop = null;
      console.error("[Tangle.main] >> Provided argument renderFunc is null or not a function.");
    }
  },

  isPaused: function isPaused() {
    return (_paused == true);
  },

  pause: function pauseMain() {
    if (!_paused) {
      _paused = true;
      clearTimeout(_updateLoopId);
      cancelAnimationFrame(_renderLoopId);
    }
  },

  play: function playMain() {
    if (_paused) {
      _paused = false;
      _updateLoopId = setInterval(_updateLoop, 1000 / 60);  // aim for 60 FPS
      _renderLoopId = requestAnimationFrame(_renderLoop);
    }
  }
};

// to use:
tangleCore.init(Game.update, Game.draw);
tangleCore.play();

Now we're starting to get somewhere! Less boilerplate code in our games == WIN.

Rubber, Meet Road

As with the last post, I've created a simple demo that shows off this new functionality.

As with AssetCache, Tangle Core should be usable in all modern browsers, plus Internet Explorer 8. Once we dive into the specifics of Canvas this may not be the case, but the basic components we've been building so far have applicability in other contexts beyond Canvas-based games. The AMD approach allows us the flexibility to cherry-pick the parts of Tangle we want to use without locking us into a particular programming paradigm.

Next time we'll talk about Screens, States, and how to avoid making your game loops look like a plate of spaghetti.

Resources

js13kGames: On Polish

Tuesday, September 11, 2012

In my last post, I said that I had the basic tile mechanics (swapping, sliding, and group matching) in place for my js13kGames entry, Blak & Bloo. At the time, I felt like the next thing I needed to tackle was polish:

To have a satisfying experience, though, requires more polish... it's going to require custom tile graphics, animations, and tweening of tile movements. So next, I'm going to need to write a Sprite class.

Me, last time

So with this in mind, I spent a few hours scouring the Internet for suitable (and usable) fruit & berry images, drawing a custom "picnic basket" style background to replace the plain black frame in the prototype, and modified the codebase to use these images in the render loop. There's a definite difference in the "before" and "after" screenshots of this change:

Blak & Bloo gameboard with basic, colored-box tiles
BEFORE

This colored-boxes version kind of looks like one of those optical illusions, doesn't it?

Blak & Bloo with improved, image-based tiles
AFTER

Overall, I'm pretty happy with the direction the art's going, although I'll want to tweak it somewhat as we go along, so it doesn't get too "busy".

Once I'd made these changes, I decided to check the js13kGames contest's Facebook page and saw that several entries had already been posted. I went over to check out what kinds of things other people were doing, and was blown away by the diversity and quality on display!

Holy Miyamoto, I've got to step up!

Hitting the 13KB Wall

Unfortunately, I got trapped in a mental cul-de-sac of sorts — I really want the polish that comes with the better graphics, but they made the payload much bigger than the contest's 13-kilobyte limit!

I tried a few things to shrink my footprint, with varying degrees of success:

  1. I base64-encoded the images as data URIs, hard coded at the head of my JavaScript code... no size savings there, because I'm using PNG images, which are already pretty efficiently stored
  2. I opened up my images in The GIMP again, and meticulously removed all the extraneous fields of color I could, if they could conceivably be drawn in by canvas fillRect() calls at runtime. This helped a bit, but not nearly enough to get my under the size limit.
  3. Finally, I realized what should have been an obvious avenue for size-reduction: I dropped the color depth of the images from 24-bit (RGB / true color) to 8-bit (limited to an optimized 256-color palette). This last avenue proved to be the most fruitful: I'm now back under the 13kb limit, with room to spare.

Along the same lines, I'm beginning to realize that my reliance on my minimal game framework, TangleJS, is making things bigger than they need to be. Case in point: the state machine code. But that deserves an entire blog post, so I'll save that for next time, probably in the post-mortem.

Two days to go!

js13kGames: Library Design and Core Mechanics

Friday, August 31, 2012

js13kGames logo Last week I started working on a game for the js13kGames competition, to see if I could do something fun and cool with open web technologies in less than 13 kilobytes. It's been an interesting week, I've learned a couple of things about my workflow, and I've almost got something playable to show off (maybe next week).

Need-Driven Library Design

The first thing I've run into is how incomplete my in-development HTML5 game library, TangleJS, still is. I've made a couple of decent games from scratch in the past, and started abstracting ideas and lessons learned there to make a better, more deliberate platform for my future efforts. I'm chronicling that process in my blog series, Let's Make a Canvas Library. The mistake I made was in thinking that it was in any way ready for me to start actually, you know, using.

The game I'm building for js13kGames, Blak & Bloo, is my first real effort to build something using the barely-just-enough-library-and-no-more philosophy that's driving Tangle's development. At every step, when I found some new feature I needed (e.g. sprite animations, state machine transition notifications, etc), rather than just grabbing something that worked in a previous game and hacking it down to fit my immediate need, I've tried to take a step back and deliberately design something more general-purpose that can be easily reused as part of the overall Tangle library. It's... well, it's slowed me down during what should have been a quick week of prototyping.

That said, I believe the extra effort I've spent doing this hasn't been wasted; on the contrary, Tangle won't ever be really useful to me unless I extend it organically by growing it on demand in this way. It's just made progress slower and less interesting than I would've liked it to be. Thus, I haven't really had much to blog about. At least, not until yesterday.

Game Mechanics

As a reminder, my vision for Blak & Bloo is a 2-player, cooperative/competitive Match-3 puzzle game. As in all games of this style, the goal will be to line up groups of 3 or more matching tiles (in this case fruits and berries) to score points. The “hook” I alluded to last time, the thing that I hope will make the game fun to play, is that Player 1 and Player 2 have totally different ways of interacting with the game... to make tile matches, Player 1 can only swap adjacent tiles, and Player 2 can only shift rows and columns along their axes:

tile-swapping vs tile column shifting

So far, I've got the code written to make these swaps and shifts (and any resulting group matching) happen instantly, and the tiles are rendering themselves as simple swatches of color, like in the image above. To have a satisfying experience, though, requires more polish... it's going to require custom tile graphics, animations, and tweening of tile movements. So next, I'm going to need to write a Sprite class.

js13kGames: Kickoff

Sunday, August 19, 2012

js13kGames logo This past Monday marked the beginning of a cool new 1-month competition called js13kGames, where the aim is to use open web technologies (i.e. JavaScript, HTML5, and CSS3 — no plugins) to build an online game. I've had a couple of ideas simmering on the back burners of my brain for the past few months, as I've been working on my Let's Make a Canvas Library blog series, and I've decided to take this contest as an opportunity to grab one of those ideas and front-burner it for the next 4 weeks. Should be a fun exercise, and when the month's over, I hope to have something neat to show for it.

Design Brainstorming

So the original kernel of my idea was to make a simple puzzle game in the Match-3 vein, something like Bejewelled maybe, but to somehow change the dynamic. The trick is, the genre is pretty crowded with innovations, so it's a little hard to come up with something that's never been done before. A less-than-innovative game could be saved with awesome artwork or music, but sadly, my artistic skills aren't quite at the level where they could redeem an otherwise-boring game, so that's not really an option.

About the same time I started trying to think of clever Match-3 game hooks, I needed to create some fake game assets for my AssetCache demo page. I hit upon the fake game name "Black & Baloo" for a title screen, and kinda liked it:

Blak & Bloo title screen prototype

The name started out as just a simple play on words, sparked by something in my gray matter about a bear named Baloo in an old favorite story, and was never intended to be any more than a throwaway for that TangleJS tutorial page. But the more I thought about it, the more I realized I could use it as the hook I was needing!

What if, I thought, I based my game around the interactions of two player characters, named Black and Baloo respectively? (I've since decided to change the names to Blak and Bloo, since the pun still works that way, and it's a little more unique). The idea of collaborative / competitive Match-3 hasn't been explored very well, I don't think, so there's a novelty factor. Plus, the two-player mechanic provides some interesting technical challenges for me to overcome, which I'll probably talk about soon.

Once I'd hit upon this core idea, my ideas began to take shape. Next time, I'll talk about another one of the game mechanics I'll be playing with to make the game (and the development process) more interesting. It's gonna be a fun month!

Let's Make a Canvas Library: Assets

Monday, July 16, 2012

Welcome back to Let's Make a Canvas Library, our tutorial series focused on the principles of building a good personal code library for writing games and other cool stuff with HTML5 Canvas. Today we're going to focus on assets: images and sounds.

Like the blog series it's modeled on, my aim is primarily educational, not to come up with an awesome library that every person on the planet is going to want to use. That said, I'll be committing each improvement to a new Github repo I'm calling Tangle, for anyone who wants to follow along.

Asset Management

preloading splash message

Unless you're really embracing your retro muse, just about any game you set out to build is going to have assets: the images and sounds that give it its unique flavor. For many games, these assets will take a while to load, especially over low-bandwidth connections like those usually found on mobile devices. That being the case, you're going to want to preload those files before starting the main game loop. And since this is something you're going to do over and over (and over) again, on basically every game you build, it makes sense to make it a part of your library.

In my first few games, my preloading routine was limited to just images and javascript modules (when I needed sounds, I did those separately, using SoundManager2 or something similar). For a while, my preload process had two phases: first, I'd pass a list of images and modules to the Preloader class, which would then call dojo.require() on the modules, and pass the list of images along to a global ImageCache object to preload separately. When all the code and images were loaded by the Dojo loader and ImageCache, then the Preloader class would signal completion back to the calling routine. In retrospect, it probably involved a few too many steps of indirection:

//
// (in main.js)
//
dojo.addOnLoad(function init_loader(){
    dojo.require("loc.Preloader");
    dojo.addOnLoad(preload);  // when loc.Preloader is loaded, run preload()
}); // end of init_loader()

function preload() {
    window.loader = new loc.Preloader({
      images: {
        title: "res/title.png",
        map:   "res/map.png",
        floor: "res/floor.png",
        tiles: "res/underworld_tiles.png",
        items: "res/items.png",
        link:  "res/link.png"
      },
      modules: ["id.MapQuest"]
    })

    if (loader.ready()) {
        // if everything was cached (or we're on a really fast network),
        // then we're ready to proceed.
        init_game();
        delete window.loader;
    } else {
        // still waiting for things to preload; listen for onReady
        var listener = dojo.subscribe("loc.Preloader.onReady", function() {
            dojo.unsubscribe(listener);
            init_game();
            delete window.loader;
        });
    }
} // end of preload()

function init_game() {
    window.game = new id.MapQuest({
        ...
    });
}

// ------------------------------------------------
// (in Preloader.js)
//
dojo.provide("loc.Preloader");
dojo.require("loc.ImageCache");
dojo.addOnLoad(function() {
    if (!window.imageCache) { window.imageCache = new loc.ImageCache(); }
});

dojo.declare("loc.Preloader", null, {
    images: {},
    modules: [],
    _isReady: false,
    constructor: function preloader_constructor(args){
        dojo.mixin(this, args);

        for (var i in this.images) {
            // treat item as a key:value pair
            window.imageCache.addImage( i, this.images[i] );
        }
        for (i in this.modules) {
            // load each requested item and then register an OnLoad handler
            //   to be notified when they're all done
            dojo.require(this.modules[i]);
        }
        dojo.addOnLoad(function() {
            if (window.imageCache.ready()) {
                dojo.publish("loc.Preloader.onReady",[]);
            } else {
                this.listener = dojo.subscribe(
                    "loc.ImageCache.onReady",
                    function() {
                        dojo.unsubscribe(this.listener);
                        delete this.listener;
                        this._isReady = true;
                        dojo.publish("loc.Preloader.onReady",[]);
                    }
                );
            }
        });
    },
    ready: function preloader_ready() {
        return this._isReady;
    }
});

// ------------------------------------------------
// (in ImageCache.js)
//
dojo.provide("loc.ImageCache");
dojo.declare("loc.ImageCache", null, {
    constructor: function ImageCache_constructor(args) {
        this._sprites = {};
        this._loadStatus = {};
    },
    addImage: function addImage(name,src) {
        if (name in this._sprites) {
            return;
        } else {
            this._sprites[name] = new Image();
            this._loadStatus[name] = false;
            this._sprites[name].src = src;
            this._sprites[name].onload = dojo.hitch(this, function() {
                this._loadStatus[name] = true;
                if (this.ready()) {
                    dojo.publish("loc.ImageCache.onReady",[]);
                }
            });
        }
    },
    hasImage: function hasImage(name) { return (name in this._sprites); },
    getImage: function getImage(name) {
        if (name in this._sprites) {
            return this._sprites[name];
        } else {
            console.error("Image name '", name, "' not found");
            return null;
        }
    },
    ready: function ready() {
        var retVal = true;
        for (var id in this._sprites) {
            if (id in this._loadStatus) {
                retVal &= this._loadStatus[id];
            } else {
                return false;
            }
        }
        return retVal;
    }
});

Making it Better

While this approach works, there are several ways we can go about improving it:

  1. If you read my previous post, you know that crafting our modules in an AMD-compatible format takes care of code preloading, so that's one less thing we need our Preloader to do.
  2. Notice that both Preloader and ImageCache have an onReady event (implemented using Dojo's publish/subscribe methods). That's a clue that we can probably collapse their functionality into a single class.
  3. While we're making changes, and given the broad browser support for the HTML5 <audio> tag, it's probably worth broadening our cache support from just images to include audio assets as well (ultimately we may want to include other types of media like video or fonts too, but that's not necessary just yet).

Bearing these ideas in mind, here's what a new, streamlined, asset-agnostic, AMD-compatible Preloader might look like (note that I've omitted a few details for the sake of brevity; you can review the whole thing over on GitHub):

define(
    ['atto/core','atto/event'],
    function(atto,CustomEvent) {
        function constructor(args) {
            var _assets     = {},
                _loadStatus = {},
                _events = {
                    error:  new CustomEvent('tangle.assetCache.error'),
                    ready:  new CustomEvent('tangle.assetCache.ready'),
                    loaded: new CustomEvent('tangle.assetCache.loaded'),
                },
                _types = {
                    IMAGE: 1,
                    AUDIO: 2
                },
                _extension_to_type = {
                    png: 1, bmp: 1,  gif: 1, jpg: 1, jpeg: 1,
                    wav: 2, webm: 2, ogg: 2, mp3: 2, aac: 2
                };

            function _assetTypeFromFilename(fileName) { ... }

            function _createLoadCallback(assetName) { ... }

            function _createErrorCallback(assetName) { ... }

            function _addAsset(name, src) {
                if (name in _assets) {
                    _events.error.dispatch({name:name,
                      details:"Asset '"+name+"' is already in the cache"});
                    return;
                } else {
                    var assetType = _assetTypeFromFilename(src);
                    switch (assetType) {
                        case _types.IMAGE:
                            _assets[name] = new Image();
                            atto.addEvent(_assets[name], 'load',
                              _createLoadCallback(name), false);
                            atto.addEvent(_assets[name], 'error',
                              _createErrorCallback(name), false);
                            break;

                        case _types.AUDIO:
                            _assets[name] = new Audio();
                            atto.addEvent(_assets[name], 'canplaythrough',
                              _createLoadCallback(name), false);
                            atto.addEvent(_assets[name], 'error',
                              _createErrorCallback(name), false);
                            break;

                        default:
                            // unsupported asset type
                            _events.error.dispatch({name:name,
                              details:"Asset type '"+assetType+"' not supported."});
                            return false;
                            break;
                    }

                    _loadStatus[name] = false;
                    _assets[name].src = src;
                }
            }

            function _hasAsset(name) { ... }
            function _getAsset(name) { ... }
            function _getAssetType(name) { ... }

            function _ready() {
                var retVal = true;
                for (var id in _assets) {
                    if (id in _loadStatus) {
                        retVal &= _loadStatus[id];
                    } else {
                        return false;
                    }
                }
                return retVal;
            }

            return {
                TYPES        : _types,
                addAsset     : _addAsset,
                hasAsset     : _hasAsset,
                getAsset     : _getAsset,
                getAssetType : _getAssetType,
                ready        : _ready,
                events       : _events
            } // end of public interface
        } // end of constructor

        return constructor;
    } // end AMD callback function
);

Let's take a look at a few of the more interesting pieces here.

AMD Wrapper

define(
    ['atto/core','atto/event'],
    function(atto,CustomEvent) {
        function constructor(args) {

            ...

            return {
                TYPES        : _types,
                addAsset     : _addAsset,
                hasAsset     : _hasAsset,
                getAsset     : _getAsset,
                getAssetType : _getAssetType,
                ready        : _ready,
                events       : _events
            } // end of public interface
        } // end of constructor

        return constructor;
    } // end AMD callback function
);

We've seen this before, it's the same basic AMD-flavored version of the Revealing Module pattern that I showed off in the previous post in this series. As in that case, I'm using AMD's define method to specify my module's dependencies, here a couple of modules from my minimal JavaScript library Atto, and return a factory function that can be used by downstream code to create an instance of my class. So far, so good.

Communicating Events

_events = {
    error:  new CustomEvent('tangle.assetCache.error'),
    ready:  new CustomEvent('tangle.assetCache.ready'),
    loaded: new CustomEvent('tangle.assetCache.loaded'),
}
...
_events.error.dispatch({name:name, details:"Asset '"+name+"' is already in the cache"});
_events.ready.dispatch({});

This is new, but not really worth going into too much detail about here. You can think of Atto's Event class as a slightly more robust implementation of the basic pub/sub pattern, but using object literals instead of strings for the topics. As the browser progresses through the preload process, AssetCache dispatches events that can be watched by the main game code to show a loading progress bar, display a status message, or to know when the assets are available to be used:

var progressBar = new ProgressBar({min: 0, max: assetCount});

cache.events.error.watch(function(data) {
   _log(data.details);
});

cache.events.loaded.watch(function(data) {
   _log('Loaded asset ' + data.name + '.');
   progressBar.setValue(progressBar.getValue() + 1);
});

cache.events.ready.watch(function(data) {
   _log('All assets loaded.');
   progressBar.setValue(progressBar.getMax());
   game.setState(game.states.TITLE);
});

Determining Load Status

That leads us to AssetCache's bread and butter, the code that actually does the asset preloading:

function _createLoadCallback(assetName) {
    return function() {
        _loadStatus[assetName] = true;
        _events.loaded.dispatch({name:assetName});
        if (_ready()) _events.ready.dispatch({});
    }
}
function _createErrorCallback(assetName) {
    return function() {
        _loadStatus[assetName] = false;
        _events.error.dispatch({name:assetName,
            details:"Asset '"+assetType+"' failed to load."});
    }
}
...
var assetType = _assetTypeFromFilename(src);
switch (assetType) {
    case _types.IMAGE:
        // image: this is by far the simple case
        _assets[name] = new Image();
        atto.addEvent(_assets[name], 'load', _createLoadCallback(name));
        atto.addEvent(_assets[name], 'error', _createErrorCallback(name));
        break;

    case _types.AUDIO:
        // audio; this will take a little more magic, due to browser codec issues
        _assets[name] = new Audio();
        atto.addEvent(_assets[name], 'canplaythrough', _createLoadCallback(name));
        atto.addEvent(_assets[name], 'error', _createErrorCallback(name));
        break;

    default:
        // unsupported asset type
        _events.error.dispatch({name:name,
            details:"Asset type '"+assetType+"' not supported."});
        return false;
        break;
}
_loadStatus[name] = false;
_assets[name].src = src;

We have some very basic code to determine file type based on its extension, then create the appropriate DOM element, Image or Audio (we should probably wrap the Audio case in a try/catch block, since we're trying to retain as much cross-browser compatibility as possible, and not all browsers support the Audio tag). Then, we attach a couple of event handlers to the element to keep track of its loading progress, and assign its src attribute to the appropriate URL to initiate the load. The Image element has pretty straightforward load and error events, but Audio, in contrast, doesn't fire a load event when it's finished loading. Instead, you get the canplaythrough event, which fires when the browser decides it has buffered enough data from the file so that if you start playing immediately, it will have the whole thing soon enough to play without stuttering. That's close enough for our purposes.

Audio Support Still Stinks

One note on preloding audio assets: AssetCache's support for audio files will depend on your browser and operating system. On my Windows 7 laptop, Google Chrome (v20) can load and play back both the mp3 and ogg audio files, but Firefox (v13) only supports the ogg file, and IE9 only supports the mp3! Neither audio file is loadable on my locked-down work computer, where I'm stuck with IE8, and I couldn't get either one to load on my stock Android 2.3 browser either. I haven't delved any deeper into the mobile issue, but I suspect that the audio object may have actually loaded, but not fired the canplaythrough event. Or maybe it couldn't load either format, I'm not entirely sure.

We'll revisit this crazy audio situation in a later tutorial to find a better cross-browser solution. You'd think that this would be an ideal use case for a library: it would be so much nicer if your app code could just specify a single, named audio asset, and let the library figure out the right file format to load based on browser and platform capabilities. In theory, the audio element exposes a method that reports on your browser's ability to play various file types, but it's notoriously vague and generally unhelpful, so relying on it to determine a caching strategy may not be very helpful.

Demo Time!

I've set up a simple demo page that shows off AssetCache's basic functionality, albeit not yet in the context of an actual game.

AssetCache should work in all modern browsers, and as I said, it even has limited support for Internet Explorer 8. Even though Tangle is just a reference implementation and not a truly production-ready library, as a general principle I try to build things that are as broadly usable as possible, so in theory parts of Tangle could be used to build a game that gracefully degrades on older, less-capable browsers. After all, the whole principle of modular dependency loading that AMD enables means that if you want to cherry-pick certain pieces that you find useful and not use the rest of the library, you're free to do so.

Next time we'll cover the basics of building a game loop, and actually use AssetCache to build something interesting.

Resources

Let's Make a Canvas Library: Modules

Wednesday, July 4, 2012

Hello and welcome to part 1 of Let's Make a Canvas Library, a tutorial series focused on the principles of building a good personal library for working with HTML Canvas.

But first, an apology: I originally announced this blog series immediately after my presentation at UtahJS on the same subject, and promised that part 1 would be going online that Friday. Obviously, it didn't.

So, what happened? I fell in love:

What's so great about AMD?

AMD, officially known as the Asynchronous Module Definition API, has been gaining steam in recent months as a solution for several longstanding issues in the JavaScript world: non-blocking script loading, dependency management, standardized module definition syntax, and global namespace abuse. The Dojo toolkit, which I've been using for the past few years, had a fairly elegant approach to modules that addresses a lot of these concerns, and it's one that I've put to good use in my personal game-development library. However, over the past year Dojo has been moving away from that format and migrating their codebase over to AMD-style modules, which are quickly becoming the de facto standard for client-side JavaScript libraries* (jQuery, MooTools, and Firebug have all built in AMD support recently). While similar in spirit, the AMD API is different enough that I was unwilling to take the time to figure it out and convert my old code, until a recent project at work gave me the opportunity to build something new from scratch.

In short, I'm an AMD convert. Everything that felt hacky to me about JavaScript before, from documenting a module's dependencies in its header comments, to including a pile of link and script tags in my HTML pages in the exact right order to ensure proper dependency preloading, has a solution in the AMD approach. Take, for example, the simple TabContainer widget I built as part of my minimal Atto library. Here's what you had to do in your HTML to use the original, non-AMD version:

<link rel="stylesheet" href="/js/atto/awAccordion.css"/>
<script src="/js/atto/aw-core.js"></script>
<script src="/js/atto/awAccordion.js"></script>
<script>
    aw.core.addLoadEvent(function() {
        var myWidget = new aw.Accordion(aw.core.byId("containerNode"), {});
    });
</script>

There are a couple of things about this not to like. First, the widget won't work without the associated CSS syles, so we either have to explicitly link to the appropriate CSS file in the invoking HTML (ugh), or execute a hack of some kind in the JavaScript to guess the correct URL for the CSS file (tricky, since a JavaScript file doesn't know its own URL location when it's being evaluated), and then load it via Ajax.

The second problem with this approach is that awAccordion.js can't stand on its own, as it relies on a window-level object called aw having been created first by its implicit dependency, aw-core.js. This object would serve as container for the new widget's factory class, but also provided necessary helpers (like _loadResource) that simplified the widget's logic and eliminated redundant code paths for pages that included multiple Atto widgets. To be sure, centralizing reusable code is a good thing, but it's pretty nasty that in order to use this (admittedly very barebones) widget, my HTML page has to reference not one, but three separate files (one CSS and two JavaScript), and use an explicit onLoad event to know when everything was actually ready to use.

Compare that to the code needed to use the updated AMD-compatible version of this same widget:

<script src="/js/require.js"></script>
<script>
    require(["atto/core", "atto/accordion"], function(atto, Accordion) {
        var myWidget = new Accordion(atto.byId("containerNode"), {});
    });
</script>

When using an AMD-compatible loader (there are several; RequireJS and curl are two of the most frequently used), you get two global functions: require() and define(). Although there are a few alternative calling syntaxes, require typically takes two arguments: a list of strings representing the resources you need, and a function to execute when those resources (and any of their dependencies) are all loaded, which eliminates the need for an onLoad event. Usually this callback function should define an argument for each module in the required list, so that the loader has a way to give you a reference to it. For example, in the above code, we're requiring two modules, "atto/code" and "atto/accordion", and the callback names these atto and Accordion (although I could have called them "kumquat" and "rutabaga" if I had wanted to, since these are just references).

One important note: outside of this callback, neither atto nor Accordion are defined or available. This keeps the global namespace clean, but may take some getting used to for programmers who are accustomed to having everything available off of the window or document objects.

The second function provided by an AMD-compatible loader is define, and is used, logically enough, to define a module. Here's a simple example of a widget factory class:

// declare the Atto Accordion widget
define(
    ["atto/core","require"], // explicitly declare Atto Core as a dependency
    function(atto) {
        // make sure the appopriate CSS has been loaded for this widget
        var forWidget = "atto-accordion";
        if (!document.querySelector("style[data-for-widget='"+forWidget+"']")) {
            require(["text!atto/accordion.css"], function(rawCss) { atto.addWidgetCss(rawCss, forWidget); });
        }

        function constructor(rootNode, optionArray) {
            // (private variables and function definitions omitted for brevity)

            return {
                root     : _root,
                addPanel : _addPanel
            } // end of public interface
        } // end of constructor

        return constructor;
    } // end function
);

The typical calling signature for define mirrors that of require: a list of strings representing the module's dependencies, and a function that is executed when those dependencies are loaded, which returns the contents of the module (in this case a constructor function for an Accordion widget). The AMD format allows you to explicitly specify a module name as the first argument to define, but current best practice is to omit it, allowing an optimization tool to fill in the name at compile time.

Okay, I get it, AMD's awesome. So what?

So my first topic for Let's Make a Canvas Library was going to be about asset management and preloading, which I had approached previously in a way that was very strongly tied to Dojo's module loader. When I became an AMD convert, I realized this was no longer the best way to solve the problem of preloading. In fact, the need for the Preloader class, which took an arbitrary list of Dojo module names and image paths and preloaded them all, all but disappeared! We'll still need an AssetCache class to handle preloading of images and sounds, but this post is long enough, so we'll save that for next time.


* Yes, I realize that AMD isn't the only option, and that CommonJS has its fans. Personally, though, I feel like AMD is easier to grok than CommonJS, so there you go.

Let's Make a Canvas Library

Sunday, June 3, 2012

This past Friday I was privileged to speak at the first ever Utah JavaScript conference, UtahJS, in Salt Lake City. My talk centered on the challenges inherent to using HTML5 Canvas to build rich internet games, and advocated the (perhaps radical) notion that instead of using one of the many, many available game frameworks or libraries, aspiring game devs would be better off writing their own:

  • you can be assured that you're not pushing any extra, unnecessary bytes to client devices (especially important on mobile)
  • you have full control over your module's interfaces, so don't have to conform to someone else's coding style
  • you get a better understanding of how things work, which becomes important when you need to debug things that go awry.

The talk was primarily focused on principles, rather than on concrete code examples, although I did have a little time at the end to briefly run through some of the code I'd written for Canvassa, which evolved into Infinite Dungeon, and which I am now porting to Fenjin. I got the sense both from questions and comments during my talk as well as several conversations afterward that people wanted a little more meat. So, let's build something together!

Let's Make a Canvas Library is a new series I'll be writing here on Palagpat Coding. Together, we'll walk through the coding of a simple HTML5 Canvas game, building up our custom game library as we go. Over the coming weeks, I expect to cover the following topics:

  • Preloading & caching game assets (images, sounds, etc)
  • Constructing the game loop
  • Managing sprites
  • Handling interactions between the player, enemies, and stage (i.e. collision detection)
  • Basic physics (and why you probably don't need Box2D)
  • Cross-platform sound
  • Multiple input/control methods

It's likely that this list will change as the game begins to take shape, and some topics will take longer to cover than others. My goal isn't to build something robust and put it on Github for all the world to use; rather, it's to walk you through building something that gets you started with your own library, custom-tailored to the kinds of things you'd want a library to do for you.

You can expect to see the first article in the series this Friday. Until then!