Touch Support for Mobile Breakout
Fri, Jun 24, 2011Adding 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:
- touchstart
- touchmove
- touchend
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.
Related Links
You can find the game here and the code is here