Touch Support for Mobile Breakout

Fri, Jun 24, 2011

Adding touch support for breakout on mobile devices introduces some interesting issues, the biggest being that the <canvas> performance on mobile devices is… well, lets just say its ‘weak’.

It is playable on an iPad2 at about 30fps, but on android devices (I tested with honeycomb on a galaxy tab 10.1) the frame rate can be as low as 5-10fps depending on the browser - and that is unacceptable.

The performance hit is all in the draw() method, which takes up to 60ms on android devices (compared to 1ms on chrome for windows desktop). Hopefully the support for hardware accelerated <canvas> will improve on mobile devices. With a bit of luck we will get an implementation of the chrome browser for android!

One conclusion of this is that a game like breakout might be better implemented with a mixture of DOM elements (for the bricks and score) and only use <canvas> for the ball, maybe not even that. But that will have to be an experiment for another day. For now implementing the touch events make it playable on iOS (on an ipad2) so at least one mobile device is now supported.

Detecting a Touch Device

To detect a touch enabled device, you can use the Modernizr library, or you can treat Modernizr more like a mentor and just learn from it:

var hasTouch = ('ontouchstart' in window);

By detecting touch support, we can show a different set of instructions to touch users with a bit of javascript and some css:

$('instructions').addClassName(hasTouch ? 'touch' : 'keyboard');
#instructions       .keyboard { display: block; }
#instructions       .touch    { display: none;  }
#instructions.touch .keyboard { display: none;  }
#instructions.touch .touch    { display: block; }

Touch Events

The touch events follow a similar pattern to mouse events:

You can attach to the events using whatever library/strategy you already use. I have an addEvent method in my Game.Runner library. So for breakout, I can start the game when the instructions are touched, and then when the game is running use the touchmove event to move the paddle

addEvent('instructions','touchstart', this.play.bind(this));
addEvent('canvas',      'touchmove',  this.movePaddle.bind(this));

The touch events include the targetTouches attribute which is a list of fingers touching the current DOM element. For breakout, I assume a single touch will place the paddle level with that touch:

movePaddle: function(ev) {
  this.paddle.place(ev.targetTouches[0].pageX - this.bounds.left);
},

Others have written about the touch events in much more detail.

A Warning about Touch Coordinates

An important note about the touch events and coordinates. Supposedly, they should provide 3 sets of coordinates: client, page and screen. It makes sense in a <canvas> game to respond to the touch events on the <canvas> element and use the client coordinates to know where on the canvas was touched.

This works great in iOS, but on android devices the clientX and clientY coordinates are incorrect, they actually contain the same values as the pageX and pageY coordinates.

Ugh! This means you can’t rely on clientX and clientY, so you have to use pageX/Y instead and convert it to canvas relative coordinates yourself.

Conclusion

Adding simple touch events is fairly straight forward, but will get more complex as you add support for more complex behaviors, so the touch events in breakout are really just the tip of the iceberg.

The biggest worry is the <canvas> performance on mobile devices. It really is not ready for arcade like action games. I can see why everyone builds native apps for mobile! Hopefully this situation will improve, but it could take a while.

The increase of interest in webGL might also provide a suitable alternative, using a hardware accelerated 3D context on the <canvas> even for 2D games might be the way to go, but that is also still in its very early stages of support.

… I think I see some native objective-C projects in my future.

You can find the game here and the code is here