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:

  • Creating a canvas element.
  • Initializing EaselJS.
  • Drawing basics.
  • Event loop and animation basics.
  • Caching keystrokes.

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>

In real world, you might want to strip js and css parts out to separate files, but I placed them inline for the sake of simplicity.
There two significant things here.

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.

no canvas, sorry
start reset raw

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.

no canvas, sorry
start reset raw

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()

no canvas, sorry
start reset raw

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 :).