Saturday, October 30, 2010

MVC is not Elegant, It's the Only Thing that Works

I was doing some Swing programming over the summer. Although I've long been familiar with MVC concepts and although I've been doing Swing programming almost since Swing first came out, it was only this summer when I *really* started understanding MVC.

In the past, I always understood MVC in terms of "elegance." Designing UI frameworks using MVC concepts supposedly resulted in a more elegant design. One could easily change the look of a component and reuse behaviours in different widgets. Separation of presentation from data and control logic led to a cleaner design where orthogonal issues would be stored in different places in the code instead of all grouped together.

In fact, this is all wrong. People don't use MVC because it is elegant. People use MVC because for certain user interfaces, MVC is the only design that works. As such, you don't have much choice. If you have a UI design that allows for two views on the same piece of data, you need MVC (for example, if you have two word processing windows open on the same document--so when you type things in one window, these changes should be reflected in the other window as well). Once you need to support this situation in a UI, then MVC just naturally falls out.

  • If you have two windows for editing the same data, you need code to let the user edit data: that's the controller.
  • You need to store the data in one spot (not separately in each window) or else it will get out of sync in the two windows: there's your data model
  • Finally, whenever the data changes, it needs to update both windows to reflect the changes: that's the view

Once you want to support two views of the same piece of data, the reasoning behind MVC becomes obvious. MVC is not elegant. In fact, for simple UIs that only need to support a single view on a piece of data, MVC is unnecessarily complicated. MVC exists because it is the only design that works for complicated UIs. They really should state this outright in documentation instead of rambling on about this elegance nonsense.

Monday, October 11, 2010

Double-Click Detection in IE and Other Browsers

Several months ago, I wrote some code for cross-browser support for double-click detection. It was sort of tricky to put together, so I should I have posted it, but I forgot. But here it is:

function hookIEDblClickToClickCatcher(object, handler)
var isDown = false;
var isMismatchedUp = false;
object.onmousedown = function() { isDown = true; isMismatchedUp = false; };
object.onmouseup = function() { if (isDown) isDown = false; else isMismatchedUp = true; };
object.ondblclick = function() { if (isMismatchedUp) {; isMismatchedUp = false; } };
object = null;

The general idea of this code is this. You might add an onclick handler to some HTML element in JavaScript. But then you notice that, in IE, if people click an object repeatedly (like in a game), every 2nd click counts as a double-click and is ignored. So you want to register a double-click handler and have that also trigger a click event.

The problem is that this results in too many clicks for non-IE browsers. Other browsers trigger both a click event and a double-click event on the second click of a double-click, so in total your click handler ends up triggering three times. So what you want is a way to trigger a click handler on a dblclick for IE only (but you don't want IE specific code in case IE changes its behavior in the future).

Fortunately, IE has a distinctive sequence of mouse down and mouse up events that lets you detect an IE double-click vs. the double-click of other browsers (there is no mouse-down event before the double-click event).

Data URI Test

In games, I want to use data URLs for images, but IE only supported this type of URL starting with IE8. Here is some code for testing for URI support. You supply a callback function, and it will be given a true or false value depending on whether data URIs are supported or not (the data URI used is a 1x1 transparent GIF--I couldn't really find anything smaller).

function testDataURLSupport(callbackfn)
var img = new Image();
img.onload = function() { callbackfn(true); callbackfn = null; };
img.onerror = function() { callbackfn(false); callbackfn = null; };
img.onabort = function() { callbackfn(false); callbackfn = null; };

Sunday, October 10, 2010

Scrolling Games in JavaScript

So I'd previously tried to program a game in JavaScript with a scrolling background, and repeatedly encountered performance problems. In the background of the game was about 100 images, positioned absolutely, that had to scroll as the player moved. These images were part of a larger map involving thousands of image tiles, so not all of the images could be created in advance--images that were not visible had to be removed and new images had to be created as for map tiles coming into view.

All the benchmark information available at the time talked a lot about how innerHTML to create new objects was much faster than manipulating the DOM by hand in JavaScript. This makes sense. JavaScript is slow, but in most browsers, DOM objects are separated from JavaScript by heavy COM or XPCOM abstraction layers, so if you can minimize the number of DOM calls you make, that should make your JavaScript faster. Based on this reasoning, I initially wrote my scrolling code by erasing all my objects and creating replacements with a single innerHTML call. Unfortunately, this was really slow (too slow for most games), but I was too busy to work out a better approach.

I recently found some time to take a second look at this, and I found that the performance issues were more comlex than that. In fact, there is an overhead for manipulating the DOM, but it's actually not too bad. Creating (and to a lesser extent deleting) DOM objects involves a much larger overhead. Also strange is that IE8 is much slower than IE6 for a lot of this DOM manipulation (probably because it's properly synchronized etc.).

So the correct way to create a scrolling background is not to restrict oneself to a single DOM call. Instead, to scroll a bunch of images, it's fastest to go through each image and move each one by an appropriate amount (by using = '?px'). It results in hundreds of DOM calls, but it's much faster than creating new images. I was thinking of creating some sort of complicated DOM hierarchy, so that I could move multiple images with a single DOM call, but that wasn't necessary--moving each image individually was sufficiently fast. Of course, you need to create new images and remove old ones as new areas come into view or move out of view, but the performance saved by creating fewer new HTML elements counteracts the cost of the extra DOM calls.

So some quicky benchmarks reveal that when 100 moves of around 25 tiles, 128x128 in size, in 5 pixel increments take this amount of time (in milliseconds) :

IE8 with innerHTML complete refresh of all objects14563
Chrome with innerHTML complete refresh of all objects5843
FF3.5 with innerHTML complete refresh of all objects10918
IE8 with simple move elements around1919
Chrome with simple move elements around605
FF3.5 with simple move elements around1691
IE8 with simple move elements and reusing old elements1641

This means that it's probably possible to do simple games in IE8, so there's no immediate need to switch to using canvas and restricting oneself to non-IE browsers. I'm hoping that developers won't program their HTML5 games using only the canvas element because I think more traditional methods are sufficiently fast and more compatible. In fact, if I do move to use HTML5 features, I'm vaguely thinking of going with SVG, but I'll have to see how the performance evolves.