Please, I was following a tutorial on how to make a snake game and I understood everything about the code up until the where the snake has to move. I just don't seem to get the logic of how the snake is moving. The code snippet is shown below:
. . . //the code block to setup the canvas and other basic stuff for the game
//creating the snake
var snake_array; //an array of cells to make up the snake
function create_snake(){
var length = 5; //length of the snake
snake_array = [] //starting with an empty array
for (var i = 0; i < length; i++) {
//this will create a horizontal snake starting from top left
snake_array.push({x:i,y:0});
}
}
create_snake() // invoking the method
//painting the snake
function paint(){
. . . // code block to clear the trail of the snake on the canvas before the snake moves
var nx = snake_array[0].x;
var ny = snake_array[0].y;
if(d == "right") nx++; //d is a variable updated by the keydown event listener code(not shown here)
else if(d == "left") nx--;
if(d == "up") ny--;
if(d == "down") ny++;
var tail = snake_array.pop(); // pops out the last cell
tail.x = nx;
tail.y = ny;
snake_array.unshift(tail)
. . . // code block to paint the snake cells
}
}
paint() // invoking the method
My problem is: How does portion of code I outlined above do the work of advancing the snake because when I tried following the code with my browser console, after the call to create_snake(), I have an array of five objects(which represent the snake cells) which have the following property and values : {x:0,y:0},{x:1,y:0},{x:2,y:0},{x:3,y:0},{x:4,y:0}. After invoking the paint method, I still have an array of five objects but with the following property and values : {x:1,y:0},{x:0,y:0},{x:1,y:0},{x:2,y:0},{x:3,y:0}. Now how does the snake move forward when d = "right"since the x property/co-ordinate of the last cell of the snake after calling paint() is 1 less than its original value before calling the method, and again, two cells now overlap since two objects in the snake_array now have the same co-ordinate({x:1,y:0})
The relevant code initiates like this:
var snake_array;
function create_snake(){
var length = 5;
snake_array = [];
for(var i = length-1; i>=0; i--){
snake_array.push({x: i, y:0});
}
}
The important thing to notice about this code is that it generates the snake backwards, so it looks like this: {x:4,y:0}, {x:3,y:0}, {x:2:,y:0}, {x:1,y:0}, {x:0,y:0}. That is because i starts at length-1 and length is set to 5 up above the for loop. This looks like:
[T][x][x][x][H][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
Then, in the paint function it grabs the tails position as it intends to move the tail to the position the head is about to be (by doing it this way, it doesn't need to move any of the other cells until the next paint, as those will never change as the snake never moves more than a single cell per cycle.)
var nx = snake_array[0].x;
var ny = snake_array[0].y;
if(d == "right") nx++;
else if(d == "left") nx--;
else if(d == "up") ny--;
else if(d == "down") ny++;
The tail of the snake is always the last element in the array under this scheme, and the head is always the first. So, snake_array[snake_array.length] is ALWAYS the tail, and snake_array[0] is ALWAYS the head.
Remembering {x:4,y:0}, {x:3,y:0}, {x:2:,y:0}, {x:1,y:0}, {x:0,y:0}, and assuming that our default movement is right, we can calculate {nx:5,ny:0}. nx and ny now represent a cell that is not part of the snake, but the cell the snake wants to move into. So the code does some bounds and collision checking on that cell to see if it intersects with any wall or other snake segments, and if it does it restarts the game.
It also checks for food collision and handles that (instead of moving the tail, it just creates a new head at the cell position and generates another piece of food.)
But, assuming this cell (nx,ny) position is not colliding with anything, it runs this code:
var tail = snake_array.pop();
tail.x = nx; tail.y = ny;
Which removes the tail from the snake_array, sets its position to the position of the cell that we calculated earlier (nx,ny) and then shifts this 'tail' into the first position of the array (which is always the head, by our convention from earlier):
snake_array.unshift(tail);
So it ends up like this:
[ ][x][x][x][H][T]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
But really, the array still is in the form of [H][x][x][x][T] because we decided anything in the first element position of snake_array[0] was the head, and anything in the last element position of snake_array[snare_array.length] was the tail. So it's really:
[ ][T][x][x][x][H]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
And then the whole process can repeat again over and over until you win or lose!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With