6
\$\begingroup\$

i made a simple js snake game with canvas, the code is all mine so it can have some drawbacks or not so good parts. I will be glad to hear some opinions.

https://github.com/vitaliyterziev/snake

var Poly = {
 //polyfill Set()
 set: function (array) {
 'use strict';
 var cleanList = [];
 var bool = false;
 array.forEach(function (itemDup) {
 cleanList.forEach(function (noDup) {
 if (noDup[0] === itemDup[0] && noDup[1] === itemDup[1]) {
 bool = true;
 }
 });
 if (!bool) {
 cleanList.push(itemDup);
 }
 bool = false;
 });
 return cleanList;
 }
};
/*
 main game object is below
*/
var SnakeGame = {
 setup: function() {
 'use strict';
 // initial variables
 var canvas = document.getElementById('basic'),
 ctx = canvas.getContext('2d'),
 gameStatus = document.querySelectorAll('h3')[0],
 gameRestart = document.querySelectorAll('#restart')[0],
 snakeLength = 5,
 snakeSpeed = 300,
 snakeWidth = 10,
 snakeHeight = 10,
 snake = [],
 x = 10,
 y = 10,
 direction = 39,
 up = 38,
 down = 40,
 left = 37,
 right = 39,
 foodCoordinates = [],
 xFoodCoordinate,
 yFoodCoordinate,
 doneMove = false,
 feeding = false,
 gameOver = false;
 // draw object, painting and clearing rectangles
 var draw = {
 fill: function(x, y) {
 ctx.fillRect(x, y, snakeWidth, snakeHeight);
 },
 clear: function(x, y) {
 ctx.clearRect(x, y, snakeWidth, snakeHeight);
 }
 };
 // gradient effects
 var grd = ctx.createLinearGradient(0, 0, 170, 0);
 grd.addColorStop(0, "yellow");
 grd.addColorStop(0.5, "green");
 ctx.fillStyle = grd;
 // initial snake coordinates
 for (var i = 0; i < snakeLength; i++) {
 draw.fill(x + (i + x * i), y);
 snake.push([x + (i + x * i), y]);
 }
 // generating the food coordinates which will be used
 for (var fcube = 0; fcube < 350; fcube++) {
 if (fcube % 11 === 10) {
 foodCoordinates.push(fcube);
 }
 }
 // event handlers
 document.onkeydown = function(key) {
 if (key.keyCode === 37 || key.keyCode === 38 || key.keyCode === 39 || key.keyCode === 40) {
 if (direction - key.keyCode != 2 && direction - key.keyCode != -2) {
 if (doneMove) {
 direction = key.keyCode;
 }
 doneMove = false; // change the direction once atleast 1 cube is drawed
 }
 }
 };
 gameRestart.onclick = function() {
 if (gameOver) {
 snake.forEach(function(el) {
 draw.clear(el[0], el[1]);
 });
 draw.clear(xFoodCoordinate, yFoodCoordinate);
 gameStatus.className = gameStatus.className.replace(' fail', '');
 gameStatus.textContent = 'Game ON';
 SnakeGame.setup();
 }
 };
 // growing function
 var feedFunc = function(eat) {
 if (eat) {
 feeding = true;
 draw.fill(snake[0][0], snake[0][1]);
 snake.unshift([snake[0][0], snake[0][1]]);
 speedFunction();
 }
 };
 // food coordinates cleaner
 var foodCoordsCleaner = function() {
 var tempFoodCoordinates = foodCoordinates.filter(function(el) {
 for (var fi = 0; fi < snake.length; fi++) {
 if (el === snake[fi][0] || el === snake[fi][1]) {
 return false;
 }
 }
 return true;
 });
 return tempFoodCoordinates;
 };
 /*
 * drawing the food cube with random x and y coordinates
 * check if there are duplicate coordinates in the snake array
 */
 var foodLoop = setInterval(function() {
 var safeFoodCoordinates = foodCoordsCleaner();
 if (!feeding) {
 draw.clear(xFoodCoordinate, yFoodCoordinate);
 }
 ctx.fillStyle = '#ffa500';
 xFoodCoordinate = safeFoodCoordinates[Math.floor(Math.random() * safeFoodCoordinates.length)];
 yFoodCoordinate = safeFoodCoordinates[Math.floor(Math.random() * safeFoodCoordinates.length)];
 draw.fill(xFoodCoordinate, yFoodCoordinate);
 ctx.fillStyle = grd;
 feeding = false;
 }, 6000);
 // end game function, clears the food loop
 var gameEndFunc = function() {
 gameOver = true;
 gameStatus.className = gameStatus.className + ' fail';
 gameStatus.textContent = 'Game Over';
 clearInterval(foodLoop);
 };
 // speed function
 var speedFunction = function() {
 switch (snake.length) {
 case 10:
 snakeSpeed = 200;
 break;
 case 15:
 snakeSpeed = 150;
 break;
 case 20:
 snakeSpeed = 100;
 break;
 case 25:
 snakeSpeed = 70;
 break;
 case 30:
 snakeSpeed = 40;
 break;
 default:
 break;
 }
 };
 /*
 * snake controll, calling gameEndFunc and feedFunc
 * clear rect, eat, draw rect, add element and remove the last one
 * draw 1 cube, push one cube and shift the last one (moving forward)
 */
 var snakeFunc = function(x, y) {
 if (x > 340 || y > 340 || x < -1 || y < -1 || snake.length !== Poly.set(snake).length) {
 gameEndFunc();
 } else {
 feedFunc(x === xFoodCoordinate && y === yFoodCoordinate);
 draw.clear(snake[0][0], snake[0][1]);
 draw.fill(x, y);
 snake.push([x, y]);
 snake.shift();
 doneMove = true;
 }
 };
 /* 
 * game loop recursive function
 * moving/coloring "11px" in direction, caling the main snakeFunc
 */
 var gameLoop = function() {
 setTimeout(function() {
 x = snake[snake.length - 1][0];
 y = snake[snake.length - 1][1];
 switch (direction) {
 case right:
 snakeFunc(x + 11, y);
 break;
 case down:
 snakeFunc(x, y + 11);
 break;
 case left:
 snakeFunc(x - 11, y);
 break;
 case up:
 snakeFunc(x, y - 11);
 break;
 default:
 console.log('Wrong direction/keypress');
 break;
 }
 if (!gameOver) {
 gameLoop();
 }
 }, snakeSpeed);
 };
 gameLoop();
 }
};
SnakeGame.setup();
#basic {
 border:2px solid #000;
 margin:20px;
 -moz-box-shadow: 0 0 3px black;
 -webkit-box-shadow: 0 0 3px black;
 box-shadow: 0 0 3px black;
}
.fail {
 color:red;
}
#restart {
 display:block;
 background-color:#228B22;
 font-family:cursive;
 font-size:16px;
}
h3 {
 font-family:cursive;
 font-size:16px;
}
<!DOCTYPE html>
<html>
<head>
 <title>Snake GAME</title>
 
 <meta name="author" content="Vitaliy Terziev">
 <link rel="stylesheet" href="css/main.css">
</head>
<body>
 <h3>Game ON</h3>
 <button id="restart">Restart Game</button>
 <canvas id="basic" width="350" height="350"></canvas>
 <script src="js/main.js"></script>
</body>
</html>

Pimgd
22.5k5 gold badges68 silver badges144 bronze badges
asked Nov 27, 2015 at 12:12
\$\endgroup\$
0

1 Answer 1

5
\$\begingroup\$

I think you should try to split your program into multiple objects like Snake, FoodFactory, Field...

And then a Game object which will "just" connect them:

snake.move(direction);
snake.draw();
if(hasCollision(snake.head, snake.queue) || 
 hasCollision(snake.head, field)){
 // display game over
}
if(hasCollision(snake.head, food.position)){
 points += 1;
 snake.increaseLength();
 do{
 food = FoodFactory.create
 while(hasCollision(snake.body, food));
}
//...

So you can separate the rules of the game (the logic) and the actual implementation.

answered Nov 27, 2015 at 13:26
\$\endgroup\$
1
  • \$\begingroup\$ this actually sounds good, thanks for your answer! Especially the 'FoodFactory' ..... catching :) \$\endgroup\$ Commented Nov 27, 2015 at 13:56

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.