Lesson #1 – Frame rate / canvas / HTML5 and iPhone

Writing a simple arcade game for a handheld device (and even then to be executed through a web browser) is quite different from the luxury of writing for the desktop. Not only do you have to consider the scale and colour of everything but you have to be careful with just how much you throw around.

I no longer use DIVs and IMG files instead opting to use the Canvas element courtesy of HTML5.

IFM screenshot

Invaders from Mars - optimised for iPhone

When I first started writing with the iPhone in mind I thought I had run in to some serious canvas limitations. I was essentially doing the same thing with the same routines but somehow the approach seemed to yield a far lower frame rate. It didn’t make sense to me since iPhone is pretty powerful, right ?
I scratched my head. Could it really be that wiping the entire screen and then redrawing everything (regardless of whether it has moved) was such a drain on the device’s performance ? I certainly didn’t want to go to the trouble of detecting which sprites occupied certain regions before clearing the canvas down. Besides that would probably be an even bigger ask.

But the it dawned on me that my methods were not ideal.

I’ll try to explain.

When I populate the game with entities I essentially pick them from a pot.
During initialisation I build arrays of entities. In most cases for things like explosions I create a pot of about 50 objects. Same goes for alien bombs and player lasers. I also tend to throw around spark effects and various other niceties.
When the game loop requests such an entity it enters a loop and looks for the object that isn’t currently visible.
Where there is a lot going on on-screen this can stretch to pretty much the array length.
When it finds an object to use it simply breaks out of the loop and returns the object.

In total I was probably asking my game loop to step through an array of around 300 elements 20 – 30 times a second.
I then pass these objects in to the relevant method – draw(), move(), collision() – and have each method handle the situation in its own container.
Where the object being passed is not visible (i.e. it’s been destroyed / picked up, left the screen boundaries or hasn’t yet been used) I simply back out of the method.

if (!object.visible) return;

This works well and obviously saves a huge amount of crunching when you’re trying to perform the same actions several times a second.

So I put together a couple of tests.
In one corner I had my current and unedited game file.
In the other corner I had a new game file with the array sizes drastically reduced. A small and insignificant changes you might think !
Both files were running flat out.

I ran them both and took the diags.
Both were running in Firefox 3.6.

The first file gave me around 77 FPS.
The second (with the reduced array sizes) gave me around 90 FPS.

I’ve yet to test this on the iPhone but I would hope to see frame rates increase from the 16 – 18 FPS that I was experiencing earlier.
Something around 25 FPS ought to be acceptable for a simple Space Invader game.

I’m rather pleased to see this since the alternative was alter the game’s design.
It’s important for me to have plenty to shoot at. The first thing to go would have been the fancy sparks and screen full of entities. Never the aliens and explosions.
I also took the decision to reduce the amount of lasers that the player can launch and have on screen at any one time. It’s not ideal but will probably make for a more intense game.
Finally I dropped the alien bomb pot from 30 to 4. Only 4 alien bombs on screen at once !

To counter this I ditched the idea of an energy bar that gets depleted with every hit from an alien bomb.
Instead I went for the classic one hit and you’re out a la Space Invaders / Galaga etc.
Again this tends towards a more intense experience.

So all in all a worthwhile exercise. It sets me up a bit more for crafting such games for mobile devices. Something I needed to do if I’m to maintain the amount of games I’m hoping to produce each month.

Advertisements
Post a comment or leave a trackback: Trackback URL.

Comments

  • Jani Hartikainen  On November 18, 2010 at 6:19 pm

    I did a dirty rect style renderer for canvas due to it being pretty slow with whole-canvas clears and redraws – as you were guessing.

    It’s not actually very tricky to do – you already know the size of each sprite you’re drawing and their position, and you probably have the background as an image (if not, draw it on a hidden canvas). You can simply draw the background only over the areas where the sprites were, and then redraw the sprites.

    I don’t know how much the performance will be effected on iPhone, since the resolution is smaller and the main bump seemed to come when the res was big, but I would assume it should give some speed.

    By the way, CSS transitions are hardware accelerated on iOS, so rendering-wise they should be the fastest.

    (why do I feel like I’ve mentioned some of this before?)

  • Nick  On November 18, 2010 at 8:25 pm

    Very exciting stuff. I’m a new programmer who started learning because of my passion for games. I’ve gotten a little side tracked with the reality of the world and I’ve been stuck in .NET websites and working with client data in sql server. After stumbling upon your blog, I’ve decided to really dedicate myself to your teachings because its exactly the stuff I want to be doing. Would I be able to contact you in the future for advice?

    Keep up the great work.
    ~Nick

    • markw1970  On November 19, 2010 at 9:50 am

      Hey Nick thanks for the comment.
      I should say that I’m not an expert coder just a bit of a hack-it-and-see when it comes to this stuff :)
      I’m learning new stuff all the time. Feel free to wade in with your ideas and stuff.
      And yeah drop me a line any time you like !
      Wilf

  • Nick  On November 24, 2010 at 8:40 pm

    Hey Wilf,

    Could you explain why the HTML5 canvas tag would be superior to a div?

    Thank you. I am loving reading your stuff.

    • markw1970  On November 25, 2010 at 2:37 pm

      HTML5 (within which canvas et al all come) and all that is the future ;-)
      I still get best performance – in terms of FPS – throwing around DIVs and IMGs within a DIV container. (Possibly due to my lazy handling of canvas wiping)
      But there is so much nice stuff you can do with the new technology. From a sprite game designer’s point of view just being able to create large spritesheets of my game characters is a nice feature.
      You get to draw lines and shapes on the canvas, easily present funky character sets for you text, rotate your sprites natively. It’s all good.
      You can even without much effort have your games played across a range of smart phones. At least those that support the standard.
      You also get native Audio support which is something amateur JS game designers such as myself have been crying out for.
      That said I still use the excellent SoundManager2 API but it (I believe) defaults to AUDIO where it’s supported.

      If you’re new to it I’d definitely look to using the new technology and don’t burden yourself with what is a very dated approach.

      • Cody  On November 25, 2010 at 4:37 pm

        You mentioned “lazy canvas wiping”… did you know that you can set a canvas’ width to it’s own width (not the style.width) and it supposedly clears the whole canvas faster than using a clearRect? It works across multiple browsers, but I haven’t done any speed tests.

        I also wonder about using the negative boolean operations on the canvas. Something is wrong with the browser implementation of clearRect. It shouldn’t be that slow.

  • markw1970  On November 25, 2010 at 5:00 pm

    Cody, I don’t follow.
    Do you have an example ?
    Sounds intruiging. I’d do anything to speed it all up.

    • Cody  On November 25, 2010 at 7:39 pm

      Basically…

      document.getElementById(“?”).width = document.getElementById(“?”).width;

      …is supposed to be faster than using clearRect on the entire canvas. Erasing small portions using clearRect for cleaning up moving sprites is most likely way faster in most cases, but for a full wipe this might be the best answer. I never tested this in IE though as they haven’t released their Preview version for XP.

  • Jani Hartikainen  On November 25, 2010 at 5:22 pm

    Cody, canvas is just slow no matter what you do. Especially larger operations like clearing the entire canvas, or a large portion of it, or drawing large things etc. is just much slower than drawing it even with something like GDI+ which is considered kinda slow.

    • Jani Hartikainen  On November 25, 2010 at 5:23 pm

      (needs an edit button)… although I must say that canvas has been getting faster, it’s much faster now than it used to be say last year, but it’s still not as fast as 2D ops in Flash or such.

  • markw1970  On November 26, 2010 at 10:41 am

    I’m really no expert on any of this but it strikes me as odd that a simple operation like clearing a rectangle (even at 30+ times a second) is slow.
    Assuming you don’t actually do a DOM look-up but pass the object for the .width technique I still can’t see how that works. I’d need to see a working example.

    With my logical head on I’d expect the slowest part of all this to be the actual drawing of the image.
    Especially where I have a spritesheet that contains multiple frames and I ask the code to find the appropriate portion of the image to present based on the character’s action / direction.
    It could be that I need to employ a different approach to manipulating images. I don’t know.
    Thanks for the input gentlemen. I need to carry out a wee bit of research.

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

%d bloggers like this: