Saving some frames by throwing a canvas at drawImage()

My next project is to be a maze game. I’d already ditched the idea of recreating a Gauntlet style romp through multiple dungeons because I wasn’t happy with the behaviour of the monsters. Getting stuck on walls and bunching up to block an open doorway was driving me mad. That’s when I started HyperGunner.

Two games later and the diversion has done me some good in that I now want to go back to the idea of a maze style game.

With all that I’ve learned in the last 2 months about controls and presentation on mobile devices I figured it ought to be a good project. The first thing to change was the screen resolution. In with 320 x 480 and out with the ridiculous 800 x 320 of the previous attempt.

I called the game Mutant Mayhem.
This is an old title for me that dates back to my days at Software Creations where a young coder and I teamed up to put 3D on a GameBoy Advance. Well, raycasting but the effect was the same. It was essentially Doom and performed beautifully. I’d created some cool weapons that the player could view as if in first person mode. I’d pixelled the hands (with twitching fingers during idle times – was very proud of that) and a whole array of monsters. I thought it was good work.
At this time we both read about how Doom’s designers had found the whole scientists over-run by lab experiments style of game a bit cliched and tired. Hence why hell came through the teleports on Mars rather than mutants running riot. But we liked the former idea and stuck with the title Mutant Mayhem. It was to be a similar game to Doom in that you run each level blasting mutants and locating keycards to escape.

Almost 10 years later I want to revisit the concept in a 2D view.
I have my level editor which I developed for the Gauntlet game and adapted it for a tighter resolution with neater graphics.

The trouble was each wall tile was around 32×32 in a 320×480 canvas. For a level with many walls the game loop was potentially performing drawImage() on each tile several hundred times per second.
Performance was atrocious.

So I threw the question to the group and found a solution.

The theory is simple.
Because I only really need to build the tile map once at the start of each level I simply create a canvas and draw the tile images to its context in a single loop.
I then pass this canvas in to the drawImage() method in the actual game loop.

Step 1: Create the new tile canvas in my init() function. (Note: I have a global namespace defined as g)

g.tileset = document.createElement("canvas");
g.tileset.setAttribute("id","tileset");
g.tileset.style.display = "none";
g.tileset.width = g.canvaswidth;
g.tileset.height = g.canvasheight;

document.getElementsByTagName("body")[0].appendChild(g.tileset);
g.tilectx = g.tileset.getContext('2d');

Notice how the canvas is not visible. Very important !

Step 2: Draw all tile images based on the level data to the canvas on level load

g.tilectx.clearRect(0, 0, g.canvaswidth, g.canvasheight);
for (var a=0;a<levels[g.level].tiles.length;a++)
{
  var o = levels[g.level].tiles[a];
  g.tilectx.drawImage(o.image,o.x,o.y);
}

You can see that all my level data is held within the levels[] array.

Step 3. Use the canvas in my main drawImage()

g.ctx.drawImage(g.tileset, 0,0);

The alternative to this would have been something like this:

for (var a=0;a<levels[g.level].tiles.length;a++)
{
  var o = levels[g.level].tiles[a];
  g.ctx.drawImage(o.image,o.x,o.y);
}

.. on every game loop. Which is a crippler !
You can see what I’ve done so far here: http://www.wilfscorner.co.uk/sandpit/mutant/

Ignore the car ;-)

About these ads
Post a comment or leave a trackback: Trackback URL.

Comments

  • Cody  On January 6, 2011 at 8:34 pm

    Very nice. Are you considering having a scrolling background for a larger map? Or you can always do the Legend of Zelda trick with static “rooms” being linked together in a grid map.

  • markw1970  On January 7, 2011 at 9:26 am

    Not sure yet. I like the idea of producing a level that is considerably larger than the viewable area but I’m not sure the game needs it.
    Early days ;-)

  • Jani Hartikainen  On January 10, 2011 at 12:07 am

    Have you tried converting the resulting canvas into an image (via getImageData())? Depending on how drawImage handles canvas vs image draw calls it could be a bit faster.

    • markw1970  On January 10, 2011 at 9:31 am

      Thanks, Jani. I haven’t tried that. Will give it a go ! I’ll try anything once if it gives a few more frames :)

  • Andrew Wooldridge  On January 11, 2011 at 8:55 pm

    Hey, I wanted to try to get in touch with you somehow :) I feel like we are on parallel paths in some ways with regards to games, canvas, etc.

    I wanted to send you a link to my RogueLike game I created for the Mozilla Game On Compo: http://www.whisperstorm.com/canvasquest2/
    please peruse the sourcecode – there might be something useful. I’d love to swap ideas/techniques for making fast canvas games with you :)

    • markw1970  On January 13, 2011 at 5:40 pm

      Hey Andrew. I played your game it’s really cool. Although I confess I didn’t progress too far. Every time I see something I like it makes me want to have a stab at creating something similar. Thing is I’m not so good at lengthy developments hence my obsession with quick fire arcade games.
      Some day I’ll sit down and figure out a Dungeon Seige style RPG with sprawling worlds and countless characters and quests.
      Perhaps there’s some scope for teaming up a few community members ??

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: