Nokia Snake with JavaScript + Canvas
Keeping with the theme of yesterday's post - "a stroll down memory lane" - I thought I'd re-create the Nokia Snake game (a distant relative of Nibbles) using JavaScript and the canvas element. When I was 16 two of my best friends had the same phone and I remember the three of us sitting around playing snake for hours. Good times!
Score: 0
The Code
Basically it's just a queue!
function Snake(canvas_id, score_id, width, height, scale) {
var game = this;
// state
var game_on = false,
level = 2,
score,
snake_color = "rgb(0, 255, 0)",
apple_color = "rgb(255, 0, 0)";
// display elements
var canvas, score_disp;
// game board width and scale (in pixels)
width = width ? width : 20;
height = height ? height : 16;
scale = scale ? scale : 30;
// directions
var NORTH = 0, EAST = 1, SOUTH = 2, WEST = 3;
// keys
var UP = 38, DOWN = 40, LEFT = 37, RIGHT = 39;
// initialize the snake in the top left
var snake = {
"x": 0,
"y": 0,
"body": [[0, 0]],
"direction": EAST,
"pending_direction": null
}
// the first apple goes in the middle
var apple = {
"x": (width / 2),
"y": (height / 2)
}
// keyboard handler
function handleKeys(e) {
var char;
var evt = (e) ? e : window.event;
char = (evt.charCode) ?
evt.charCode : evt.keyCode;
if (char > 36 && char < 41) {
handleChar(char);
return false;
};
return true;
}
// character specific keyboard handling
function handleChar(char) {
if (!game_on)
return;
switch (char) {
case UP:
if (snake.direction != SOUTH)
snake.pending_direction = NORTH;
break;
case DOWN:
if (snake.direction != NORTH)
snake.pending_direction = SOUTH;
break;
case LEFT:
if (snake.direction != EAST)
snake.pending_direction = WEST;
break;
case RIGHT:
if (snake.direction != WEST)
snake.pending_direction = EAST;
break;
}
}
function reset() {
// reset all values
snake.x = snake.y = 0;
snake.body = [[0, 0]];
snake.direction = EAST;
apple.x = (width / 2);
apple.y = (height / 2);
score = 0;
}
// drawing routine
function draw() {
// if paused, keep checking every 1/10th second for unpause
if (!game_on) {
t = setTimeout(function() { draw(); }, 100);
return;
}
// get reference to drawing area
if (canvas.getContext){
// create drawing context
var ctx = canvas.getContext('2d');
if (snake.pending_direction !== null) {
snake.direction = snake.pending_direction;
snake.pending_direction = null;
}
// move snake
switch (snake.direction) {
case NORTH:
snake.y--;
break;
case EAST:
snake.x++;
break;
case SOUTH:
snake.y++;
break;
case WEST:
snake.x--;
break;
}
// fill game board black
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, width * scale, height * scale);
// push new position onto stack and pop last
var old_pos = snake.body.pop();
snake.body.unshift([snake.x, snake.y]);
// test to see if snake is touching itself
for (var i = 1; i < snake.body.length; i++)
if (snake.body[i][0] == snake.x &&
snake.body[i][1] == snake.y)
reset();
// test to see if out of bounds
if (snake.x < 0 || snake.x >= width ||
snake.y < 0 || snake.y >= height)
reset();
ctx.fillStyle = snake_color;
//draw snake
for (var i = 0; i < snake.body.length; i++)
ctx.fillRect (snake.body[i][0] * scale, snake.body[i][1] * scale,
scale, scale);
// test if snake eats apple and if so, reset apple and make snake grow
if (snake.x == apple.x && snake.y == apple.y) {
snake.body.push(old_pos);
score += parseInt(level);
var free_space = false;
while (!free_space) {
free_space = true;
apple.x = (Math.floor(Math.random() * width));
apple.y = (Math.floor(Math.random() * height));
// make sure apple is draw in free space, not on top of snake
for(i = 1;i < snake.body.length; i++)
if(snake.body[i][0] == apple.x && snake.body[i][1] == apple.y)
free_space = false;
}
}
// draw apple
ctx.fillStyle = apple_color;
ctx.fillRect (apple.x * scale, apple.y * scale, scale, scale);
// display score
score_disp.innerHTML = score;
}
// calculate timeout using level
t = setTimeout(function() { draw(); }, 120 - (20 * level));
}
this.toggle_play = function() {
if(game_on == true) {
game_on = false;
} else {
game_on = true;
}
return game_on;
}
this.change_level = function(new_level) {
level = new_level;
}
this.initialize = function() {
canvas = document.getElementById(canvas_id);
score_disp = document.getElementById(score_id);
// create handlers
document.onkeydown = function(e) { return handleKeys(e) };
document.onkeypress = function(e) { return handleKeys(e) };
reset();
draw();
}
}
Comments (4)
Charles | nov 04 2010, at 04:47pm
Dang, man, have you been looking at my plan for world domination?
Haiko Schol | nov 04 2010, at 01:21pm
Nice! Please add massive multiplayer capabilities, achievements, allow user to contribute levels with voting and integrate it with Facebook. Then I can remove this from my project ideas list. ;)
Ken Swift | nov 04 2010, at 12:17pm
Thats awsome man!
Commenting has been closed.
Greg Bergé | nov 05 2010, at 04:58pm
Hello, great job your games are very impressive ! I work on a AS3 API in JS/canvas, may be you can help me in contributing to the code or in making some game with my API ? You can contact me via my website.
You can check out the actionJS project here : https://github.com/neoziro/actionJS