5
\$\begingroup\$

I am a beginner in JavaScript and Phaser and wrote a Snake game. I think there are lot of things I could have done better. I am looking for some advice on how I can improve the design and refactoring of the code. You can play the game here.

var mainGame = {
 preload: function() {
 game.load.image('snake', 'images/snake.png');
 game.load.image('apple', 'images/apple.png');
 },
 create: function() {
 // data members
 this.snake = [];
 this.initialSnakeLength = 3;
 this.squareSize = 15;
 this.speed = 9;
 this.count = this.speed;
 this.width = Math.floor(game.world.width / this.squareSize);
 this.height = Math.floor(game.world.height / this.squareSize);
 this.cursors = game.input.keyboard.createCursorKeys();
 this.direction = "right";
 this.apple = game.add.sprite(0, 0, 'apple');
 this.apple.visible = false;
 this.score = 0;
 this.scoreText = game.add.text(10, 10, "", fontStyle);
 this.updateXY = {up: {x: 0, y: -1}, down: {x: 0, y: 1}, left: {x: -1, y: 0}, right: {x: 1, y: 0}};
 this.createSnake();
 this.drawScore();
 this.drawApple();
 },
 update: function() {
 this.updateDirection();
 this.moveSnake();
 this.snakeEatApple();
 this.checkGameOver();
 },
 checkGameOver: function() {
 this.checkOutOfWorld();
 this.checkSnakeCollision();
 },
 checkSnakeCollision: function() {
 var head = this.snake[this.snake.length - 1];
 for (var i = 0; i < this.snake.length - 1; ++i) {
 if (head.x == this.snake[i].x && head.y === this.snake[i].y) {
 this.gameOver();
 }
 }
 },
 checkOutOfWorld: function() {
 var head = this.snake[this.snake.length - 1];
 if (head.x < 0 || head.x > game.world.width || head.y < 0 || head.y > game.world.height) {
 this.gameOver();
 }
 },
 gameOver: function() {
 finalScore = this.score;
 game.state.start('gameOver');
 },
 drawScore: function() {
 this.scoreText.setText("Score: " + this.score);
 },
 createSnake: function() {
 for (var i = 0; i < this.initialSnakeLength; ++i) {
 this.snake.push(this.add.sprite(game.world.centerX + i * this.squareSize, game.world.centerY, 'snake'));
 }
 },
 snakeEatApple: function() {
 var head = this.snake[this.snake.length - 1];
 if (head.x === this.apple.x && head.y === this.apple.y) {
 this.score += 1;
 this.speed = Math.max(4, this.speed - Math.floor((this.score) / 5)); // increment speed
 this.growSnake();
 this.drawScore();
 this.drawApple();
 }
 },
 growSnake: function() {
 var tail = this.snake[0];
 var x = this.snake[this.snake.length - 1].x + this.updateXY[this.direction].x * this.squareSize;
 var y = this.snake[this.snake.length - 1].y + this.updateXY[this.direction].y * this.squareSize;
 this.snake.push(game.add.sprite(x, y, 'snake'));
 },
 moveSnake: function() {
 --this.count;
 if (this.count === 0) {
 this.count = this.speed;
 var tail = this.snake.shift();
 tail.x = this.snake[this.snake.length - 1].x + this.updateXY[this.direction].x * this.squareSize;
 tail.y = this.snake[this.snake.length - 1].y + this.updateXY[this.direction].y * this.squareSize;
 this.snake.push(tail);
 }
 },
 updateDirection: function() {
 if (this.cursors.up.isDown && this.direction != "down") {
 this.direction = "up";
 }
 else if (this.cursors.down.isDown && this.direction != "up") {
 this.direction = "down";
 }
 else if (this.cursors.left.isDown && this.direction != "right") {
 this.direction = "left";
 }
 else if (this.cursors.right.isDown && this.direction != "left") {
 this.direction = "right";
 }
 },
 drawApple: function() {
 var posX = game.rnd.integerInRange(0, this.width - 1);
 var posY = game.rnd.integerInRange(0, this.height - 1);
 var collidingWithSnake = false;
 for (var i = 0; i < this.snake.length; ++i) {
 if (posX === this.snake[i].x && posY === this.snake[i].y) {
 collidingWithSnake = true;
 break;
 }
 }
 if (!collidingWithSnake) this.apple.reset(posX * this.squareSize, posY * this.squareSize);
 else this.drawApple();
 }
}
Tolani
2,5017 gold badges31 silver badges49 bronze badges
asked Oct 16, 2016 at 21:40
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

This is really excellent code for a beginner. The high-level structure is clear, and that's one of the most important and difficult things to get right.

This isn't a full critique, but I did notice one function where you can remove a lot of repitition. You can rewrite:

updateDirection: function() {
 if (this.cursors.up.isDown && this.direction != "down") {
 this.direction = "up";
 }
 else if (this.cursors.down.isDown && this.direction != "up") {
 this.direction = "down";
 }
 else if (this.cursors.left.isDown && this.direction != "right") {
 this.direction = "left";
 }
 else if (this.cursors.right.isDown && this.direction != "left") {
 this.direction = "right";
 }
},

as:

updateDirection: function() {
 const opposites = {down: 'up', up: 'down', left: 'right', right: 'left' };
 const directions = Object.keys(opposites);
 const isPressed = d => this.cursors[d].isDown;
 const isLegal = d => this[d] != opposites[d];
 const newDir = directions.find(d => isPressed(d) && isLegal(d));
 if (newDir) this.direction = newDir;
},

Note: the above is untested, but hopefully helpful. Also note, if needed for performance, you can pull the constants opposites and directions and isPressed and isLegal out of the function, so they won't be recreated each time it's called. The "work" of the function is all accomplished in the final two lines.

answered Oct 16, 2016 at 22:36
\$\endgroup\$

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.