Another JavaScript Game: Snake
One of my favorite projects to assign ADDA students is to create a game from scratch using JavaScript. I'm always surprised by the ambition of what they want to build. This semester, two of my students decided to take on snake. So I got to thinking how I would approach the same problem. Once I start down this path its impossible for me to stop. I stuck closely to the original rather than build a game more like slither.io, which is the direction that one of my students went.
No Canvas?
I made one decision that seems odd to me in retrospect. I eschewed the <canvas>
and made the game board out of html div
elements instead. I did this as a model to my students on what could be done with the tools they already know. When I started I was unsure if it would even work, but I'm pleasantly surprised by the outcome.
Snake Movement
I created an object constructor for the snake.
function Snake() {
//this is the starting location for the snake
//first number is x location, second is y
this.locations = [[25, 25], [24, 25], [23, 25], [22, 25], [21, 25], [20, 25], [19, 25], [18, 25], [17, 25]];
//hold the direction the snake is moving
this.dir = {
x: 1,
y: 0
};
this.food = [];
}
So when I wanted to move the snake, I just added the direction to the current 'head,' and then added that value to the beginning of the array.
Snake.prototype.draw = function() {
//get current 'head' location
var x = this.locations[0][0]
var y = this.locations[0][1];
//increment by the direction
var head = [x + this.dir.x, y + this.dir.y];
//add new head to the locations array
this.locations.unshift(head);
}
To change the tail, I simply just pop out the last element in the locations array.
Snake.prototype.cutTail = function() {
return this.locations.pop();
}
To make the animation happen, I just add or remove a class to the head and tail div.
Speeding It Up
Originally I just had the snake move at a constant speed, but wanting to keep true the original, I had to have the snake speed up as the score increased.
I had been using a setInterval
to call my render function repeatedly, but once the program is started, there is no way to dynamically alter the duration of that function, or at least I couldn't find a way. Instead I created a recursive setTimeout
function to achieve this.
function draw(speedFactor, cb){
var speed = setSpeed(speedFactor);
console.log(speed);
cb(function(){
render();
draw(score, setTimeout)
}, speed);
}
speedFactor
is the current score. Another function takes the score and uses it to set the speed.
function setSpeed(sFactor){
var speed = 130 - (sFactor * 10)
if(speed <= 0){
speed = 10;
}
return speed;
}
Together these two functions allow me to vary the speed, so as the score increases, the interval between firings will decrease.
Conclusion
Even though this project was a distraction others things that I should have been working on, it was a fun to bang out.
If you are interested the project repository is posted here.