When I started this little project I had only a vague idea of how it would look and even less of an idea how it would be written or structured. As it turned out I just slapped down a quick gradient background, came up with a simple Bubble
class and set a timer to move the bubbles up the screen. That gave me a basic core to work from, but as I started to tweak and add I realised just how messy it was, even for a simple project like this.
So this tutorial is going to ignore all my mistakes and lead you gently to the finished version with something resembling logical sense. This entry in particular will focus on giving us a basic framework to work within, and go as far as putting a background on screen. Part 2 will introduce the render loop and actual animation.
Setting the stage
It’s tempting to just throw everything into a function and get going – in fact that’s pretty much what I did. this is fine for small-scale projects but a really bad habit to get into. When you’ve got multiple scripts all executing on one page if you aren’t careful you can get all kinds of interference. It’s best to keep things as self-contained and neat as possible, and this tutorial shows you one method of doing just that – the Revealing Module pattern.
This pattern is the same concept as the module pattern in that it focuses on public & private methods. The only difference is that the revealing module pattern was engineered as a way to ensure that all methods and variables are kept private until they are explicitly exposed.
If you haven’t mad much exposure to object-oriented languages like Java that statement might not make a lot of sense to you. Don’t worry, it will.
The aim of the pattern is to create a self-contained lump of code that is only partially accessible from code lying outside it. This has all kinds of benefits but the main one for me is in forcing you to think more deeply and carefully about how your code is structured and how various bits of it interact. In the case of Bubbles, we enter that lump of code at the start and never leave – it’s perfectly self-contained and so should play nicely with other scripts.
But let’s back up a bit. First of all we need a canvas to work with, which means HTML (and a little CSS). Our HTML file is extremely simple:
<!DOCTYPE html> <html> <head> <title>Gabi Bubbles</title> <script src='bubbles.js'></script> <link rel="stylesheet" href='bubbles.css' /> </head> <body> <canvas id="frame" /> </body> </html>
Title, script, stylesheet, canvas. As bare-bones as you can get. Now the stylesheet:
* { margin: 0; padding: 0; overflow: hidden; } body, html { height:100%; } #frame { position: absolute; }
This isn’t actually as simple as it looks. We want the canvas to be full-screen, and this code does so without any inconvenient scrollbars or weird sizing issues. It can take a while to get to this point, so take a moment to look it over.
And finally, the script itself. The snippet below is enough to get us started:
var World = (function() { var pub = {}; pub.init = function() { alert("Just imagine some bubbles for now"); } return pub; }()); window.onload = World.init;
As if often the case with Javascript this script does a lot of things in only a few lines. It defines a “module” called World
, which contains a single variable, pub
. This variable is returned by the module so only properties and methods of pub
are accessible when calling the module. So calling World.init()
actually calls World
, which returns pub
, and then init()
is called on that. Phew.
A little background
All very well, but it doesn’t look very good now does it? Let’s add a background to the mix. We’re going to use a simple gradient fill, but because it will eventually be drawn every frame and a gradient is reasonably expensive we will render it once to a separate canvas which we’ll then draw directly onto the main canvas each frame. Believe it or not blitting one image onto another is often significantly faster than drawing primitive shapes or painting operations. In my circle-based canvas demos switching from rendering circles each frame to rendering them once and then drawing the image each frame resulted in a serious performance improvement (in the order of 25%) when viewed on a phone (Nexus 4, hardly a slouch).
var World = (function() { var width; var height; var context; var background; var pub = {}; pub.init = function() { setupCanvas(); var background = getBackground(); context.drawImage(background, 0, 0, width, height, 0, 0, width, height); } function setupCanvas() { width = document.body.clientWidth; height = document.body.clientHeight; var canvas = document.getElementById("frame"); canvas.width = width; canvas.height = height; context = canvas.getContext("2d"); } function getBackground() { var background = document.createElement("canvas"); background.width = width; background.height = height; var backCtx = background.getContext("2d"); var gradient = backCtx.createLinearGradient(0, 0, 0, height); gradient.addColorStop(0, "#9DD9DA"); gradient.addColorStop(1, "#38828D"); backCtx.fillStyle = gradient; backCtx.fillRect(0, 0, width, height); return background; } return pub; }()); window.onload = World.init;
OK, so we’ve added quite a bit here. Note that World
has some new fields functions – these are all private and cannot be accessed by outside code. All we’re doing is getting the canvas element, configuring it, then creating a new canvas for the background. On this background we draw a gradient, and then we draw background onto the main canvas.
A touch of class
The last thing we’re going to do in this session is to add the first of several classes to the code. The Theme
class is a simple configuration class that will provide a central point for altering/tweaking the look and feel of the game. For now, we create an instance of the theme in our init
function and use it to style the gradient. The full code for our game so far is below:
var World = (function() { //------------------------------------------// // Theme // //------------------------------------------// function Theme() { this.backColTop = "#9DD9DA"; this.backColBottom = "#38828D"; } var width; var height; var context; var background; var theme; var pub = {}; pub.init = function() { setupCanvas(); theme = new Theme(); var background = getBackground(); context.drawImage(background, 0, 0, width, height, 0, 0, width, height); } function setupCanvas() { width = document.body.clientWidth; height = document.body.clientHeight; var canvas = document.getElementById("frame"); canvas.width = width; canvas.height = height; context = canvas.getContext("2d"); } function getBackground() { var background = document.createElement("canvas"); background.width = width; background.height = height; var backCtx = background.getContext("2d"); var gradient = backCtx.createLinearGradient(0, 0, 0, height); gradient.addColorStop(0, theme.backColTop); gradient.addColorStop(1, theme.backColBottom); backCtx.fillStyle = gradient; backCtx.fillRect(0, 0, width, height); return background; } return pub; }()); window.onload = World.init;
The theme class is contained within the World module, so there’s no risk of it interfering (or being interfered with by) another script. Now if we want to change the colours used in the background we have a convenient point at the top of the code to tweak the values.
So, today we got a gradient background filling a page. Hardly earth-shaking. But it sets the groundwork for the rest of the game and gives us a decent framework to build up from. If you’re like me and are approaching Javascript from more sane and structured languages then having something like the revealing module pattern in place, and being in the habit of using classes from the very start makes things immeasurably easier in the long run. Yes, the finished game could be written with a couple of functions and probably in half the space, but I’d guarantee that if I came back to it in two months’ time I wouldn’t understand half of what I’d written.
The finished version of today’s script can be found here.
Stay tuned
Today a static gradient, tomorrow the world actual bubbles.