Last active
April 30, 2021 23:37
-
-
Save abdelfattahradwan/6774dc33a5797ba896a3389e0c0a1444 to your computer and use it in GitHub Desktop.
A simple Snake game made using HTML and JavaScript.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<style> | |
#game-canvas { | |
position: absolute; | |
left: 50%; | |
top: 50%; | |
transform: translate(-50%, -50%); | |
} | |
</style> | |
<title>Snake</title> | |
</head> | |
<body> | |
<canvas id="game-canvas" width="512" height="512"> | |
</canvas> | |
<script> | |
/** @type {HTMLCanvasElement} */ | |
const CANVAS = document.getElementById("game-canvas"); | |
const CONTEXT = CANVAS.getContext("2d"); | |
const SEGMENT_WIDTH = 32.0; | |
const SEGMENT_HEIGHT = 32.0; | |
const COLUMNS = CANVAS.width / SEGMENT_WIDTH; | |
const ROWS = CANVAS.height / SEGMENT_HEIGHT; | |
class Segment { | |
/** | |
* @param x {number} | |
* @param y {number} | |
* @param direction {number} | |
*/ | |
constructor(x, y, direction) { | |
this.x = x; | |
this.y = y; | |
this.direction = direction; | |
this.oldDirection = this.direction; | |
/** @type {Segment} */ | |
this.next = null; | |
} | |
setDirection(direction) { | |
this.oldDirection = this.direction; | |
this.direction = direction; | |
} | |
move() { | |
if (this.direction === 0) this.y--; | |
else if (this.direction === 1) this.y++; | |
else if (this.direction === 2) this.x--; | |
else if (this.direction === 3) this.x++; | |
if (this.x < 0) this.x = COLUMNS - 1; else if (this.x > COLUMNS - 1) this.x = 0; | |
if (this.y < 0) this.y = ROWS - 1; else if (this.y > ROWS - 1) this.y = 0; | |
this.oldDirection = this.direction; | |
if (this.next !== null) { | |
this.next.move(); | |
this.next.setDirection(this.oldDirection); | |
} | |
} | |
} | |
const ROOT = new Segment(COLUMNS / 2, ROWS / 2, 0); | |
window.addEventListener("keydown", event => { | |
if (event.code === "KeyW") { | |
if (ROOT.direction !== 1) ROOT.direction = 0; | |
} else if (event.code === "KeyS") { | |
if (ROOT.direction !== 0) ROOT.direction = 1; | |
} else if (event.code === "KeyA") { | |
if (ROOT.direction !== 3) ROOT.direction = 2; | |
} else if (event.code === "KeyD") { | |
if (ROOT.direction !== 2) ROOT.direction = 3; | |
} | |
}); | |
function addSegment() { | |
let lastSegment = ROOT; | |
while (lastSegment.next !== null) lastSegment = lastSegment.next; | |
let xOffset = lastSegment.direction === 2 ? 1 : lastSegment.direction === 3 ? -1 : 0; | |
let yOffset = lastSegment.direction === 0 ? 1 : lastSegment.direction === 1 ? -1 : 0; | |
lastSegment.next = new Segment(lastSegment.x + xOffset, lastSegment.y + yOffset, lastSegment.direction); | |
} | |
let foodX = 0; | |
let foodY = 0; | |
function randomizeFoodPosition() { | |
foodX = Math.floor(Math.random() * (COLUMNS - 1)); | |
foodY = Math.floor(Math.random() * (ROWS - 1)); | |
} | |
randomizeFoodPosition(); | |
let gameOver = false; | |
let elapsedFrames = 0; | |
function update() { | |
if (elapsedFrames > 14) { | |
ROOT.move(); | |
if (ROOT.x === foodX && ROOT.y === foodY) { | |
addSegment(); | |
randomizeFoodPosition(); | |
} | |
let currentSegment = ROOT.next; | |
while (currentSegment !== null) { | |
if (ROOT.x === currentSegment.x && ROOT.y === currentSegment.y) { | |
gameOver = true; | |
break; | |
} | |
currentSegment = currentSegment.next; | |
} | |
elapsedFrames = 0; | |
} else elapsedFrames++; | |
} | |
function render() { | |
CONTEXT.fillStyle = "black"; | |
CONTEXT.fillRect(0.0, 0.0, CANVAS.width, CANVAS.height); | |
let currentSegment = ROOT; | |
while (currentSegment !== null) { | |
CONTEXT.fillStyle = "rgb(155, 55, 55)"; | |
CONTEXT.fillRect(currentSegment.x * SEGMENT_WIDTH, currentSegment.y * SEGMENT_HEIGHT, SEGMENT_WIDTH, SEGMENT_HEIGHT); | |
currentSegment = currentSegment.next; | |
} | |
CONTEXT.fillStyle = "rgb(55, 55, 155)"; | |
CONTEXT.fillRect(foodX * SEGMENT_WIDTH, foodY * SEGMENT_HEIGHT, SEGMENT_WIDTH, SEGMENT_HEIGHT); | |
} | |
function tick() { | |
update(); | |
render(); | |
if (!gameOver) window.requestAnimationFrame(tick); | |
} | |
tick(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment