Skip to content

Instantly share code, notes, and snippets.

@ssaurel
Created January 18, 2024 15:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ssaurel/e01731169b9f0dca1a3fb19ba6e8f3a2 to your computer and use it in GitHub Desktop.
Save ssaurel/e01731169b9f0dca1a3fb19ba6e8f3a2 to your computer and use it in GitHub Desktop.
Complete Snake Source Code in HTML5/JavaScript for SSaurel's Blog
<html>
<head>
<title>Snake on SSaurel's Blog</title>
<style type="text/css">
#title {
width: 400px;
text-align: center;
margin: auto;
margin-top: 20px;
margin-bottom: 50px;
font-size: 20px;
font-weight: bold;
}
#content {
width: 800px;
border: 1px solid black;
margin: auto;
}
</style>
</head>
<body>
<div id="title">Snake on SSaurel's Blog</div>
<div id="content">
<canvas id="board" />
</div>
<script type="text/javascript">
function randomInteger(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
var secondspassed, oldtimestamp, realfps, then, now, elapsed;
class Snake {
constructor(ctx, bw, bh, nbx, nby, fps) {
this.ctx = ctx;
this.bw = bw;
this.bh = bh;
this.eltw = bw / nbx;
this.elth = bh / nby;
this.nbx = nbx;
this.nby = nby;
this.dirx = 1;
this.diry = 0;
this.marginx = this.eltw / 10;
this.marginy = this.elth / 10;
this.keyup = false;
this.keydown = false;
this.keyleft = false;
this.keyright = false;
this.startfps = fps;
this.init();
}
init() {
this.head = { x: this.nbx / 2, y: this.nby / 2 };
this.tail = { x: this.nbx / 2 - 2, y: this.nby / 2 };
this.elements = [this.tail, { x: this.nbx / 2 - 1, y: this.nby / 2 }, this.head];
this.food = this.generatefood();
this.points = 0;
this.level = 5;
this.fps = this.startfps;
this.fpsinterval = 1000 / this.fps;
}
generatefood(nbx, nby) {
// check food is not on the snake
var self = this;
var touch = true;
var food = null;
while (touch) {
food = { x: randomInteger(0, this.nbx), y: randomInteger(0, this.nby) };
this.elements.every((element) => {
if (food.x == element.x && food.y == element.y) {
touch = true;
return false;
}
touch = false;
return true;
});
}
return food;
}
draw(realfps) {
this.ctx.fillStyle = "#EFEFEF";
this.ctx.fillRect(0, 0, canvas.width, canvas.height);
this.ctx.font = "20px Arial";
this.ctx.fillStyle = "black";
//this.ctx.fillText("FPS: " + realfps, 10, 30);
this.ctx.fillText("Points: " + this.points, 10, 30);
this.ctx.fillStyle = "green";
var eltw = this.eltw;
var elth = this.elth;
var marginx = this.marginx;
var marginy = this.marginy;
var ctx = this.ctx;
this.elements.forEach(function (element) {
ctx.fillRect(element.x * eltw + marginx, element.y * elth + marginy, eltw - marginx, elth - marginy);
});
this.ctx.fillStyle = "red";
ctx.fillRect(this.food.x * eltw + marginx, this.food.y * elth + marginy, eltw - marginx, elth - marginy);
}
move() {
var dirx = this.dirx;
var diry = this.diry;
// if the snake eats the food
if (this.head.x == this.food.x && this.head.y == this.food.y) {
// add a new tail
var newtail = { x: this.head.x - dirx, y: this.head.y - diry };
this.elements.unshift(newtail);
this.tail = newtail;
this.food = this.generatefood(this.nbx, this.nby);
this.points++;
if (this.points % this.level == 0) {
this.fps++;
this.fpsinterval = 1000 / this.fps;
}
}
// if the head touch one part of the elements
var self = this;
var touch = false;
this.elements.every((element) => {
if (self.head != element && self.head.x == element.x && self.head.y == element.y) {
touch = true;
return false;
}
return true;
});
if (touch) {
// we restart the game
this.init();
return;
}
var newx = this.head.x + dirx;
var newy = this.head.y + diry;
if (newx >= this.nbx) {
newx = 0;
} else if (newx < 0) {
newx = this.nbx - 1;
}
if (newy >= this.nby) {
newy = 0;
} else if (newy < 0) {
newy = this.nby - 1;
}
var newhead = { x: newx, y: newy };
this.elements.shift(); // remove last
this.elements.push(newhead); // add new head
this.head = newhead;
}
gameloop(timestamp) {
// Calculate the number of seconds passed since the last frame
secondspassed = (timestamp - oldtimestamp) / 1000;
oldtimestamp = timestamp;
// Calculate fps
realfps = Math.round(1 / secondspassed);
if (this.keyup) {
this.dirx = 0;
this.diry = -1;
} else if (this.keydown) {
this.dirx = 0;
this.diry = 1;
} else if (this.keyleft) {
this.dirx = -1;
this.diry = 0;
} else if (this.keyright) {
this.dirx = 1;
this.diry = 0;
}
now = window.performance.now();
elapsed = now - then;
if (elapsed > this.fpsinterval) {
// Get ready for next frame by setting then=now, but also adjust for your
// specified fpsInterval not being a multiple of RAF's interval
then = now - (elapsed % this.fpsinterval);
this.move();
this.draw(realfps);
}
var self = this;
window.requestAnimationFrame(function (timestamp) {
self.gameloop(timestamp);
});
}
pressdown(e) {
switch (e.key) {
case "ArrowUp":
this.keyup = true;
break;
case "ArrowRight":
this.keyright = true;
break;
case "ArrowDown":
this.keydown = true;
break;
case "ArrowLeft":
this.keyleft = true;
break;
}
}
pressup(e) {
switch (e.key) {
case "ArrowUp":
this.keyup = false;
break;
case "ArrowRight":
this.keyright = false;
break;
case "ArrowDown":
this.keydown = false;
break;
case "ArrowLeft":
this.keyleft = false;
break;
}
}
}
const canvas = document.getElementById("board");
const ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;
var snake = new Snake(ctx, canvas.width, canvas.height, 30, 30, 7);
addEventListener("keydown", (event) => {
snake.pressdown(event);
});
addEventListener("keyup", (event) => {
snake.pressup(event);
});
window.requestAnimationFrame(function (timestamp) {
then = window.performance.now();
snake.gameloop(timestamp);
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment