Reza Saadati
Reza Saadati

Reputation: 5429

Snake moves horizontally

I am trying to create a simple snake game.

(function() {
  var canvas = document.getElementById('canvas'),
      ctx = canvas.getContext('2d'),
      x = 0,
      y = 0,
      speed = 2; 
      x_move = speed,
      y_move = 0,                          
      food_position_x = Math.floor(Math.random() * canvas.width / 10) * 10,
      food_position_y = Math.floor(Math.random() * canvas.height / 10) * 10,
      size_x = 10;    

  function eat() {   
   console.log('food_x:' + food_position_x + ' x:' + x + ' / food_y:' + food_position_y + ' y:' + y);
    if (Math.floor(y / 10) * 10 == food_position_y && Math.floor(x / 10) *10  == food_position_x) {  
    	size_x += 2;
      //throw new Error("MATCH!"); // This is not an error. Just trying to stop the script
    }
  }
  
  // Drawing
  function draw() {
    eat();
    requestAnimationFrame(function() {      
      draw();      
    });    
    // Draw the snake
    ctx.beginPath();
    ctx.rect(Math.floor(x/10)*10, Math.floor(y/10)*10, size_x, 10);
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = '#ffffff'; 
    ctx.fill();
    ctx.closePath();

    // Draw the food
    ctx.beginPath(); 
    ctx.rect(Math.floor(food_position_x/10)*10, Math.floor(food_position_y/10)*10, 10, 10);
    ctx.fillStyle = "blue";
    ctx.fill();
    ctx.closePath();

    // Increase the value of x and y in order to animate
    x = x + x_move;
    y = y + y_move;       
  } 
  draw();

  // Key Pressing
  document.addEventListener('keydown', function(event) {
    switch(event.keyCode) {
      case 40: // Moving down
        if (x_move != 0 && y_move != -1) {
          x_move = 0;
          y_move = speed;
        }
      break;
      case 39: // Moving right
        if (x_move != -1 && y_move != 0) {
          x_move = speed;
          y_move = 0; 
        }
      break;
      case 38: // Moving top
        if (x_move != 0 && y_move != 1) {
          x_move = 0;
          y_move = -speed; 
        }
      break;
      case 37: // Moving left
        if (x_move != 1 && y_move != 0) {
          x_move = -speed;
          y_move = 0; 
        }
      break;
    }
  });
})();
canvas { background-color: #000022 }
<canvas id="canvas" width="400" height="400"></canvas>

jsfiddle

The problem

Every time when I catch the food, the snake becomes longer but when you press the down or up key, it moves horizontally.

Maybe a solution

This is what I believe the solution could be: The snake should be an array! Every time when the key is pressed, define the position of HEAD of snake and move the snake step by step, because it is an array. So the body follows the head. But in this case, I have no idea how to make an array from it.

Maybe there are other solutions. Any helps would be appreciated!

Upvotes: 23

Views: 684

Answers (6)

user7847084
user7847084

Reputation:

I make the game using C++ soi can advice make a new object for every snake tile and on move set it to parent position

function Tile(x, y)
{
  this.x = x;
  this.y = y;
}

Upvotes: 1

a stone arachnid
a stone arachnid

Reputation: 1286

Have a length variable and make that the x or y size based on direction, like this:

(function() {
  var canvas = document.getElementById('canvas'),
      ctx = canvas.getContext('2d'),
      x = 0,
      y = 0,
      speed = 2,
      x_move = speed,
      y_move = 0,                          
      food_position_x = Math.floor(Math.random() * canvas.width / 10) * 10,
      food_position_y = Math.floor(Math.random() * canvas.height / 10) * 10,
      size_x = 10,
			size_y = 10,
			snake_length = 10;    

  function eat() {   
   console.log('food_x:' + food_position_x + ' x:' + x + ' / food_y:' + food_position_y + ' y:' + y);
    if (Math.floor(y / 10) * 10 == food_position_y && Math.floor(x / 10) *10  == food_position_x) {  
    	snake_length += 2;
      //throw new Error("MATCH!"); // This is not an error. Just trying to stop the script
    }
  }
  
  // Drawing
  function draw() {
    eat();
    requestAnimationFrame(function() {      
      draw();      
    });    
    // Draw the snake
    ctx.beginPath();
    ctx.rect(Math.floor(x/10)*10, Math.floor(y/10)*10, size_x, size_y);
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = '#ffffff'; 
    ctx.fill();
    ctx.closePath();

    // Draw the food
    ctx.beginPath(); 
    ctx.rect(Math.floor(food_position_x/10)*10, Math.floor(food_position_y/10)*10, 10, 10);
    ctx.fillStyle = "blue";
    ctx.fill();
    ctx.closePath();

    // Increase the value of x and y in order to animate
    x = x + x_move;
    y = y + y_move;       
  } 
  draw();

  // Key Pressing
  document.addEventListener('keydown', function(event) {
    switch(event.keyCode) {
      case 40: // Moving down
        if (x_move != 0 && y_move != -1) {
          x_move = 0;
          y_move = speed;
					size_x = 10;
					size_y = snake_length;
        }
      break;
      case 39: // Moving right
        if (x_move != -1 && y_move != 0) {
          x_move = speed;
          y_move = 0; 
					size_x = snake_length;
					size_y = 10;
        }
      break;
      case 38: // Moving top
        if (x_move != 0 && y_move != 1) {
          x_move = 0;
          y_move = -speed; 
					size_x = 10;
					size_y = snake_length;
        }
      break;
      case 37: // Moving left
        if (x_move != 1 && y_move != 0) {
          x_move = -speed;
          y_move = 0; 
					size_x = snake_length;
					size_y = 10;
        }
      break;
    }
  });
})();
canvas { background-color: #000022 }
<canvas id="canvas" width="400" height="400"></canvas>

Upvotes: 1

NoOorZ24
NoOorZ24

Reputation: 3277

Well as I had some free time to spare I created my own JS snake to demonstrate you how it can be done. Most important parts are in this.snakeBody where array of body is stored and this.moveForward() where you can see how body is updated.

https://jsfiddle.net/nooorz24/p8xtdv3h/13/

moveForward: function() {
    var next = this.getNextfieldValue();

    if (next == "frame" || next == "body") {
        console.log("You lose!")
        this.isAlive = false;
    } else {
        var newHead = this.getNextfieldCoords();
        this.draw.snake(newHead.x, newHead.y);
        this.body.unshift(newHead);
        if (next == "food") {
            this.generateFood();
            this.snakeSize++;
        } else {
            var last = this.body.pop();
            this.draw.empty(last.x, last.y);
        }
    }
},

I tried to make it as readable as I could, but note that this is unfinished example and would need need loads of improvements to be a playable game

Upvotes: 7

Kyle B
Kyle B

Reputation: 2899

To debug your down or up problem, I would add some console.log('message') calls in your code for debugging.

Specifically, add some console messages in the switch statement so that you know the correct event and branch is firing. For Example:

switch(event.keyCode) {
  case 40: 
    console.log('down');
    ...
  case 39: 
    console.log('right');
    ...
}

Then add more and more debugging log messages until you find your problem.

You can view the console messages in the F12 developer tools in your browser of choice while running your game.

Also make sure you are not calling ctx.translate(...) on the canvas context to flip the canvas, this would change the direction of the x and/or y axis.

Upvotes: 0

Munim Munna
Munim Munna

Reputation: 17556

You need to maintain an array of the points currently occupied by the snake body, and add new point (unshift) to the array as the snake approaches and remove point from the back of the array (pop). The following code is a starter, you need to make it your own :).

(function () {
    const COLORS={ SNAKE:'#ff7bf5', FOOD:'blue' };
    var canvas = document.getElementById('canvas'),
        ctx = canvas.getContext('2d');
    var snake=[], score=0;
    var x, y, food_position_x, food_position_y, x_move, y_move;
    var frameCount=0, framesRequiredToMove=10;
    //the less the framesToMove the faster the sname moves
    function draw(){
        if(++frameCount==framesRequiredToMove){
            frameCount=0;
            move();
        }
        requestAnimationFrame(draw);
    }
    function init(){
        snake = [{x:3,y:0},{x:2,y:0},{x:1,y:0},{x:0,y:0}];
        snake.forEach((p)=>{plot(p.x,p.y,COLORS.SNAKE)})
        x=snake[0].x;y=snake[0].y;
        score=0;x_move=1;y_move=0;
        scoreboard.innerText=score;
        newfood();
        setTimeout(draw,1000);
    }
    function plot(x,y,color){
        ctx.beginPath();
        ctx.rect(x * 10, y * 10, 10, 10);
        ctx.fillStyle = color;
        ctx.fill();
        ctx.closePath();
    }
    function move(){
        snakepx.innerText = x;
        snakepy.innerText = y;
        x = x + x_move;
        y = y + y_move;
        // Advance The Snake
        plot(x,y,COLORS.SNAKE);
        snake.unshift({x:x,y:y});
        // Check food encounter
        if(x==food_position_x && y==food_position_y){
            scoreboard.innerText=++score;
            newfood();
        }
        else{
            var last=snake.pop();
            ctx.clearRect(last.x * 10, last.y * 10, 10, 10);
        }
    }
    function newfood(){
        food_position_x=Math.floor(Math.random() * canvas.width / 10);
        food_position_y=Math.floor(Math.random() * canvas.height / 10);
        plot(food_position_x,food_position_y,COLORS.FOOD);
        foodpx.innerText = food_position_x;
        foodpy.innerText = food_position_y;
    }
    init();

    // Key Pressing
    document.addEventListener('keydown', function (event) {
        event.preventDefault();
        switch (event.keyCode) {
            case 40: // Moving down
                if (x_move != 0 && y_move != -1) {
                    x_move = 0;
                    y_move = 1;
                }
                break;
            case 39: // Moving right
                if (x_move != -1 && y_move != 0) {
                    x_move = 1;
                    y_move = 0;
                }
                break;
            case 38: // Moving top
                if (x_move != 0 && y_move != 1) {
                    x_move = 0;
                    y_move = -1;
                }
                break;
            case 37: // Moving left
                if (x_move != 1 && y_move != 0) {
                    x_move = -1;
                    y_move = 0;
                }
                break;
        }
    });
})();
canvas {
    background-color: #000022;
    float: left;
}
<canvas id="canvas" width="400" height="180"></canvas>
<div style="margin-left: 410px">
    Snake: (<span id="snakepx"></span>, <span id="snakepy"></span>)<br>
    Food: (<span id="foodpx"></span>, <span id="foodpy"></span>)<br>
    Score: <span id="scoreboard"></span>
</div>

Upvotes: 12

camelCase
camelCase

Reputation: 49

I think that you have hit on the answer with an array. In the past, I have found that Snake works best with an array, tracking each block of the snake and checking for any two blocks being in the same position. For movement, however, you must directly control the head, and have the body follow based on the position of the array space one ahead of it. The snake head would be the first item in the array. I am not sure what you mean about not understanding the implementation of the array, but the coordinates for each block would be an item in the array.

Upvotes: 3

Related Questions