Lessons Learned from Five Years of Building HTML5 Games
Welcome, my name is... (etc.)
- A lead game developer at Uken Games
- One of the first hires at Uken
- Working with Web Apps and Games for the past 6 years
- Talking about some of the lessons learned over the past 5 years building games with web technology
Toronto's Largest Independent Game Studio
A little about Uken
- 5 years old, grown to around 65 people
- Toronto's largest Independant Game Studio
- Building on revenue
- 10 games out, most HTML5 based
- 7 platforms
Games with Web Technology
Experiences building, lanuching and supporting HTML5 Games
Many top games on mobile platforms are using HTML5
"agonisingly slow-loading assets"(Edge Magazine review of TowerUp, February 1 2012)
So you want to make a game?
What platform will you build for?
How will you package your game?
- iOS and Android fastest growing games market
- 2.9X growth in 2013
- Pure web or hybrid app? (Hybrid: native app wrapper around a WebView)
- Hybrid offers better distribution and access to payments, push notifications, more sensors
- these are the decisions we went with, and what I'll be focusing on, however the lessons should apply more broadly as well
Canvas vs DOM
Canvas
- Direct control of rendering
- Slow & inconsistent
-
WebGL (not ready for mobile)
DOM
- Rapid development & familiar UI
- Hardware accellerated CSS3 animations
Next choice, Canvas vs DOM
Canvas
- The canvas element came with the promise of direct rendering control
- Slow: Large canvas elements can be big performance hogs on mobile when trying to maintain a high framerate
- canvas graphics libraries are still not that mature when it comes to more advanced techniques like double buffering and dirty rectangles
- Inconsistent: requestAnimationFrame isn't supported everywhere (iOS 6+, Android 4.4+), and it's not fully reliable on all platforms. Just like intervals and timeouts, the literal time passed between frames isn't very reliable (especially on mobile)
- WebGL: isn't supported on the major mobile platforms. Other than in custom browsers.
- http://caniuse.com/webgl (no, basically only reasonable on Chrome or FF)
DOM
- accessible: everyone knows HTML, especially artists.
- Allows different team members to work more collaboratively, blurred lines between designers and developers
- rapid prototyping and development
- built in object model
- CSS3 animations can be hardware accellerated
Verdict
- Our benchmarks have been mixed.
- DOM has a higher overhead of creating new elements, and can slow down when there's a high number of animated elements on screen
- Canvas better for many elements, but can struggle maintaining framerate when drawing a large area every frame
- Ultimately we've found we can get the best performance for the games we've made using DOM and CSS3
CSS3 Animations
.walking {
-webkit-backface-visibility: hidden;
-webkit-animation-name: 'walkingMove';
-webkit-animation-duration: 4800ms;
-webkit-animation-timing-function: linear;
-webkit-animation-iteration-count: infinite;
}
@-webkit-keyframes 'walkingMove' {
0% { -webkit-transform: translate3d(660px,0px,0px) scale3d(1,1,1)}
45% { -webkit-transform: translate3d(170px,0px,0px) scale3d(1,1,1)}
52% { -webkit-transform: translate3d(170px,100px,0px) scale3d(-1,1,1)}
96% { -webkit-transform: translate3d(660px,100px,0px) scale3d(-1,1,1)}
100% { -webkit-transform: translate3d(660px,0px,0px) scale3d(1,1,1)}
}
- high level animation (tweening instructions, not painting)
- hardware acceleration for CSS3 (most iOS, Android 3.0+)
- smooth, fast (not blocked)
- rendering not tied to intervals/timeouts (which are inconsistent)
- Characters from Mighty Monsters (top down map)
- Sprite animations (walk) created using step-start transition timing
-
NOTE Hardware acceleration can be turned off, and performance generally degraded, as battery levels drop
This was put together by our artists using a program called Sencha Animator. It gives them a flash-like interface for creating CSS keyframe animations.
Unlike other frameworks it's not reliant on fat javascript frameworks.
Exports clean CSS.
Another DOM Example
(function animloop() {
requestAnimFrame(animloop);
game.update();
})();
Game.prototype.update = function() {
var currentTime = (new Date).getTime();
var delta = (currentTime - this.lastUpdate) / 1000;
this.lastUpdate = currentTime;
this.updater(delta);
};
Further DOM example: Super Flappy Doge
Put together by coop student Justin Vanderheide in about 1 week
Might recognise, simple flappy bird clone
Animation done by updating translate3d CSS property on elements
Another DOM Example
Mover.prototype.update = function(delta) {
this.position.x += this.velocity.x * delta;
this.position.y += this.velocity.y * delta;
this.velocity.x += this.acceleration.x * delta;
this.velocity.y += this.acceleration.y * delta;
var transform = "translate3d(" +
this.position.x * WIDTH_100 + "px," +
this.position.y * HEIGHT_100 + "px, 0) " +
"rotate(" + Math.atan2(this.velocity.y, ROTATION_LIMITER) + "rad)";
this.element.style.webkitTransform = transform;
};
High Performance UI
3 Options:
- Store all UI (HTML, CSS, JavaScript) locally
- Use client side templating and data binding
- Cache all content and only refresh what's needed
- To compete with native games, need a UI that feels responsive
- We've used all 3
- Local: If doing an offline only game, bundle UI in app (or use application cache - WordSwap)
- Data Binding: We've largely moved towards using AngularJS for new games
- Cache Content: Approach taken on existing games
- link prefetching generally not yet supported on mobile
Caching Content
function LoadBody(url, targetId) {
var $currentArea = $(".content_area:visible");
var $target = $("#"+targetId);
if (target_id == $current_area.attr("id") || $target.is(":empty")) {
$.get(url, function( data, status, xhr ) {
LoadSuccess($target, data);
});
} else { LoadSuccess($target, ""); }
}
function LoadSuccess($target, data){
if (data) { $target.html(data); }
$(".content_area").hide();
$target.show();
//request to reload any potentially changed content
}
- Very basic approach
- Can extend with watch attributes that trigger a full refresh
- And with preloading pages, updating in the background (trade off data usage for performance)
Image Preloading
Problems:
-
Browsers load images with progressive image rendering
Mobile Caching isn't very good
Sometimes browsers refuse to load images
- remote or large assets can be slow to render
- get image (or other asset) pop in
- Mostly images, but page rendering (or other assets: fonts) can also be a factor with multi-part page rendering
- iOS won’t render offscreen or "display: none;" images
- <img> tags will not be loaded, even if they’re visible, if the device is low on memory
- large images loaded in 1024x1024 chunks on iOS
- mighty map sprite example
http://www.codinghorror.com/blog/2005/12/progressive-image-rendering.html - Jeff Atwood
http://grinninggecko.com/developing-cross-platform-html5-offline-app-1/ (appcache limits)
Image Preloading
Solutions:
- Write your own cache in the native client
- Use background-image not <img>
- Preload in a “visible” container, that has a low z-index
#preload {
position: absolute;
left: 0px;
top: 0px;
height: 1px;
width: 1px;
z-index: 0;
}
Warning: background-image bypasses built in memmory limits on UIWebview.
- gets around browser issues for image preloading
- A custom native client can cache images, and you can ship important images with your build
- Don't show the page until window.onload is fired. Or use the iOS webViewDidFinishLoad() method. Every browser is a bit different.
http://www.codinghorror.com/blog/2005/12/progressive-image-rendering.html - Jeff Atwood
http://grinninggecko.com/developing-cross-platform-html5-offline-app-1/ (prefetch limits)
Browsers Everywhere
-
Pro: Every modern device has a browser
-
Pro: Almost all mobile browsers use WebKit
-
Con: Not all WebKits are created equaltranslate3d on iOS = hardware accelerationtranslate3d on Android 2.x = broken keyboard
LESSON: Great reach for games, but hitting different platforms is more difficult then it should be
Great thing, everything has a browser, generally WebKit (not always equal)
More recent Android issue: during CSS animations on *some* modern Android browsers (4.2+), elements would tile - fail to repaint previous element locations (like old windows errors)
Other odd bugs: BB10 - Most compliant HMTL5 browser when launched, drop downs didn't work!
Also: not only different performance, but degrade performance differently
Platform Fragmentation
Charts form Apple and Google, so take with a grain of salt
Can't limit yourself to only the latest and greatest (eg, Android)
Represents only 2 main platforms
Android finally has websocket and requestAnimationFrame support
The horror of BlackBerry
Even so, still many different targets with very different browser experiences (next slide)
Platform Fragmentation
iOS
- 3 form factors
- 5 resolutions
Android
-
Many form factors
-
Many resolutions
- Phone specific tweaks
Take a closer look at main two platforms, still have...
Not exclusive to HTML5, but HTML5 isn't a magic bullet either
Game/App Interfaces need to be tighter then documents
Some HTML5 specific things
- Need *some* responsive design, OR scrolling (scalling often not handled well - TowerUp)
- CSS Authoring framework is very userfull (Compass)
Thank you
Questions?
Special Thanks:
Adam Proctor
Simon Brooks
Justin Vanderheide
Reveal.js (https://github.com/hakimel/reveal.js)