Epsilon

Designing Snake in HTML5:Basic Canvas Usage

Posted on November 4, 2012

This post will show the process that I went through to get basic canvas functionality. To begin, I will show the basic HTML page that I am using for this project.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type='text/javascript' src='xui.js'></script>
<script type='text/javascript'>
    var extras = {
        first: function() { return this[0]; },
        last:  function() { return this[this.length - 1]}
    }
    xui.extend(extras);
</script>
<script type='text/javascript' src="snake.js"></script>
</head>
<body>
<div id='snake'></div>
</body>
</html>

As you can see, it’s pretty minimal, but there are a few things of interest. The first is the set of JavaScript tags, and what they all link to. The first JavaScript page I link to is called xui.js. Xui is a minimal JavaScript library that I’m using to do only a few things. The first major thing I use it for is to tie the initialization of my game for when the whole webpage is done loading, images and everything. The second thing I use it for is basic access of some of the HTML element I’ve placed on this page. Finally, I also use it to tie events such as keyboard presses into my game. The second javascript tag is a little extension to the features that Xui has to allow me to access html elements more easily. Finally, there is the script tag that links to snake.js. This will be the file that my coffeescript will compile into.

The last real thing of interest is how I will be placing my game into the webpage. In the body tag of the webpage, there is a div tag that has the id “snake”. This is the div tag that I will be placing my game into. The reason why I start out with the div tag, instead of a canvas is because later into the game process I plan to use simple html to show the score of the game. This will make placing that kind of html easier.

Webpage Initializing

Now that we have a basic idea of what the webpage will be like, we need to setup code to place a canvas element into the webpage. This is the basic coffeescript:

initialize = (div_selector) ->
    #initialize the div to be a certain size, and make it contain a canvas element
    div = x$(div_selector)
    div.css({border:'2px solid black', width:Window.WIDTH.toString() + "px"})

    canvas_stuff = "<canvas id='snake_canvas' tabindex='1'></canvas>"
    div.html(canvas_stuff)

    canvas = x$("#snake_canvas").attr('width', Window.WIDTH).attr('height', Window.HEIGHT)
    x$(document).on('keydown', keyboard_callback)

    #tie the context to the game
    context = canvas.first().getContext("2d")
    canvas.first().focus()
    Window.context = context
    Window.div = div

    TitleScreen.initialize()
    GameScreen.initialize()
    Window.screen = TitleScreen
    window.animLoop(run)

x$.ready(() -> initialize("#snake"))

So this code describes the basic initialization that must be done before we can have a game. This code creates the canvas element, places a border around the div tag we have referenced. You can also see that I initialize some of the undefined parameters I showed for the Window object I described last post. Finally, there is code for setting up the run loop for the game, where the game’s state gets updated, and finally drawn to the canvas.

Canvas Draw and Update Loop

To set up the webpage to constantly call the canvas’ update loop, there are a few compatibility issues to mess with. The HTML5 specification states that a browser should have a method called requestAnimationFrame, that is to be used for tying in drawing on canvases to the browser. Unfortunately, this is still not evenly implemented on all browsers so we have to workaround this problem. To do this I took a bit of script from here, and rewrote it in coffeescript so that I could use it in Snake. Here is the code:

raf = window.requestAnimationFrame     ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame    ||
    window.msRequestAnimationFrame

window.animLoop = (render, element) ->
    running = undefined
    last_frame = new Date
    window_loop = (now) ->
        #stop rendering if the render function returned false at some point
        if running == false
            return

        if raf
            raf(window_loop, element)
        else
            setTimeout(window_loop, 16)

        now = if now and now > 1e4 then now else +new Date
        deltaT = now - last_frame

        if deltaT < 160
            running = render(deltaT, now)
        last_frame = now
    window_loop()

So it’s a little confusing, but this code defines a function that takes in my draw/update loop function, and will call it using the best method available to the browser. If the browser doesn’t really have a way to use the requestAnimationFrame method, I fall back on a regular setTimeout call to call the update loop.

There you have it! The basic setup required to draw stuff on a canvas in this Snake game.

Checkout this github project to see the code for this project.

comments powered by Disqus