Friday, April 29, 2011

Making HTML5 Games Match Your Screen

Sand Trap was our first opportunity to try supporting multiple resolutions, when we chose to enter it into SPIL Games' HTML5 Contest, geared towards mobile devices. At the time I decided to make it fluidly adjust to match any resolution it was opened on. This way it could not only match any width and height combination of a mobile device but also any resolution of a conventional computer browser. It worked well for Sand Trap, so we're re-using some of the same tricks on Thwack!!

Thwack!! on a maximized 1024x768 browser window
Implementing this requires taking advantage of CSS and JavaScript. Using just CSS, filling the whole screen is trivial, but CSS didn't allow us to maintain the same width-to-height ratio to prevent stretching of the gameboard, so that's where the JavaScript comes in.

Since we're not completely concerned about the exact width or height, the first piece of information we need to set is the ratio of width to height. In Thwack!!, we have it set to 4/3 (the game area is 4 units wide to 3 units high). Once we have determined this, it's a matter of making adjustments whenever the document is resized or, in the case of mobile devices, the screen orientation is changed. We handle these events by setting:
 window.addEventListener('resize', resizeGame, false);
 window.addEventListener('orientationchange', resizeGame, false);
Now we create a "resizeGame" function to handle these two events. With Thwack!! it's a bit more complicated since we're using multiple canvases, but if the game is running on a single canvas, it might look something like this:
 function resizeGame()
 {
  var gameBoard        = document.getElementById('canvas');
  var widthToHeight    = 4 / 3;

  var newWidth         = window.innerWidth;
  var newHeight        = window.innerHeight;
  var newWidthToHeight = newWidth / newHeight;

  if (newWidthToHeight > widthToHeight)
  {  // window width is too wide relative to desired game width
      gameBoard.style.height = newHeight + 'px';
      newWidth               = newHeight * widthToHeight;
      gameBoard.style.width  = newWidth + 'px';
  } else {  // window height is too high relative to desired game height
      gameBoard.style.width  = newWidth + 'px';
      newHeight              = newWidth / widthToHeight;
      gameBoard.style.height = newHeight + 'px';
  }

  // center the canvas
  gameBoard.style.marginTop  = (-newHeight / 2) + 'px';
  gameBoard.style.marginLeft = (-newWidth / 2) + 'px';
 };
Thwack!! on mobile Safari
Basically, if the window is too tall, we make width 100% of the window; if the window is too wide, we make height 100% of the window. The remaining dimension is sized according to the width-to-height ratio we previously set.

The last part re-centers our canvas in the middle of the screen. Many of the CSS properties we are concerned with are manipulated directly by the function above, but in order for those to work, we set up a few other CSS properties as follows:
 #canvas
 {
  position: absolute;
  left:     50%;
  top:      50%;
 }
This allows us to put the top left corner of the canvas in the center of the screen, and then our resizeGame() function gives the canvas a negative top and left margin half of the width and height of the game board so it is centered in the window.