09 Dec 2012 Using EaselJS to create a simple arcade game, part I: The Basics.
This is the first part of a series about using EaselJS to create a simple HTML5/js based arcade game.
In this part, we'll learn about:
First things first: We need a piece of HTML to work on. It looks just like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!doctype html> <html> <head> <title>EaselJS Tutorial, Part 1</title> <script type="text/javascript" src="http://code.createjs.com/easeljs-0.5.0.min.js"></script> <script type="text/javascript"> // our easeljs code goes here </script> </head> <body> <canvas id="main" width="480" height="40" style="background: #444"> no canvas avail. </canvas> </body> </html> |
First is the inclusion of minified EaselJS library. For this example, I've used a whole bundle from their CDN, but in bigger applications you might consider getting the sources (which split the library into many smaller parts) and include only things you really need.
The second thing is the canvas
element. You can read more about it here. All we really need now is a canvas with id (we'll use it later in js) and dimensions. The text inside is a "fallback" in case the browser doesn't support canvas element properly.
OK, now that we have our HTML set up (that's probably the last piece of HTML written during those tutorials :-)) it is time to initialize EaselJS. The "central" part of EaselJS is a Stage,
1 2 |
canvas = document.getElementById "main" stage = new createjs.Stage canvas |
which should be supplied with our canvas element. Stage is our main container, holding all items we'd like to draw on the screen (including other containers). Everything that is a DisplayObject can be added to the Stage.
To actualy draw something on the screen, we'll use the Shape object. In this part we are going to draw a plate that's used to by the player. The plate is simply a rectangle, so we just need to use drawRect
method.
1 2 |
plate = new createjs.Shape plate.graphics.beginFill("#01EFFF").drawRect 0, 0, 60, 10 |
Note that the Shape doesn't really know anything about the element we are drawing. It holds only it's position and transformation properties. The element itself is stored in graphics
property.
The graphics
property holds an instance of Graphics object. It's good to keep that in mind, because if we need multiple instances of the same element, this allows us to create it once and supply it to as many Shape instances as desired.
Then we position our plate in the bottom-center and add it to the Stage
1 2 3 4 |
plate.x = (canvas.width - 60) / 2 plate.y = canvas.height - 10 stage.addChild plate stage.update() |
and it is about time to see something.
That's nice, but we don't want a static game, do we? So it's time to get moving.
To do it, we'll need a function that will get called every once in a while and update our Stage. This function is called tick
and for now it will just move our plate circularly from left to right.
1 2 3 |
window.tick = -> plate.x = (plate.x + 1) % canvas.width stage.update() |
Note that we attached the tick
function to window
, because we have to have it's holder to add it as a listener to the Ticker interface.
1 2 3 |
createjs.Ticker.addListener window createjs.Ticker.useRAF = yes createjs.Ticker.setFPS 60 |
And the Ticker is a static class which provides interface for a global event loop (a heartbeat) of our application. When we add an object, exposing tick
method, to it, this tick
method will automatically get called every tick, which in our case is calculated to provide ~60 frames per second (as set with setFPS
).
The useRAF
property is about requestAnimationFrame()
support. You'll want to set it to true
, because it's better and even if the browser doesn't support it, EaselJS will automatically fall back to the old setTimeout()
method.
Now let's allow our player to move the plate.
To do so, we'll have to catch a left/right keystroke and change our plate.x
position accordingly. It is done by subscribing to onkeyup
and onkeydown
events, exposed by the browser itself, and then testing against event.keyCode
property.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
KEY_LEFT = 37 KEY_RIGHT = 39 dx = [0, 0] _setDX = (e, dx1, dx2) -> if !e # A little hack for possible strange browser behaviour e = window.event switch e.keyCode when KEY_LEFT then dx[0] = dx1 when KEY_RIGHT then dx[1] = dx2 document.onkeydown = (e) -> _setDX e, -1, 1 document.onkeyup = (e) -> _setDX e, 0, 0 |
After that, we'll update the tick
function.
1 2 3 4 5 6 |
border = canvas.width - 60 window.tick = -> plate.x += dx[0] + dx[1] if plate.x < 0 then plate.x = 0 if plate.x > border then plate.x = border stage.update() |
You might ask why we used separate dx value for left and right, while it can be achieved by setting dx to -1/1 on left/right key down and to 0 on key up. The problem is, when user is fast enough (and it's not that hard), he can trigger opposite key down right before current key up. Say our current key down was left. User quickly pushes right and releases up and our dx goes -1/1/0. Dah! We're not moving until the second right key down event comes.
In the end, we'll make our code objective, so it won't cause us a headache later.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
class Main KEY_LEFT = 37 KEY_RIGHT = 39 constructor: (name) -> @dx = [0, 0] @canvas = document.getElementById name @plate = new createjs.Shape @plate.graphics.beginFill("#01EFFF").drawRect 0, 0, 60, 10 @border = @canvas.width - 60 document.onkeydown = (e) => @_setDX e, -1, 1 document.onkeyup = (e) => @_setDX e, 0, 0 init: -> @plate.x = @border / 2 @plate.y = @canvas.height - 10 @stage = new createjs.Stage @canvas @stage.addChild @plate createjs.Ticker.addListener @ createjs.Ticker.useRAF = yes createjs.Ticker.setFPS 60 reset: -> @stage.removeAllChildren() createjs.Ticker.removeAllListeners() @stage.update() tick: -> @plate.x += @dx[0] + @dx[1] if @plate.x < 0 then @plate.x = 0 if @plate.x > @border then @plate.x = @border @stage.update() _setDX: (e, dx1, dx2) -> if !e e = window.event switch e.keyCode when KEY_LEFT then @dx[0] = dx1 when KEY_RIGHT then @dx[1] = dx2 window.main = new Main "main" |
That's all for now, stay tuned for the next part :).