贪吃蛇游戏中的蛇移动

3
请看,我正在跟随一个关于如何制作贪吃蛇游戏的教程,并且我已经理解了代码的所有内容,直到蛇移动的部分。我似乎无法理解蛇移动的逻辑。以下是代码片段:
. . . //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

我的问题是:我上面概述的代码部分如何使蛇前进?因为当我尝试在浏览器控制台中跟踪代码时,在调用create_snake()后,我得到了一个包含五个对象(代表蛇身体单元格)的数组,它们具有以下属性和值:{x:0,y:0}{x:1,y:0}{x:2,y:0}{x:3,y:0}{x:4,y:0}。在调用paint方法之后,我仍然有五个对象的数组,但是它们的属性和值如下:{x:1,y:0}{x:0,y:0}{x:1,y:0}{x:2,y:0}{x:3,y:0}。现在,当d = "right"时,蛇如何向前移动,因为在调用paint()之后,蛇的最后一个单元格的x属性/坐标比调用该方法之前少了1,并且再次出现两个重叠的单元格,因为蛇数组中的两个对象现在具有相同的坐标({x:1,y:0}).
1个回答

1
相关的代码像这样开始:

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});
    }
}

这段代码的重要之处在于它以相反的顺序生成蛇,所以看起来像这样:{x:4,y:0}{x:3,y:0}{x:2:,y:0}{x:1,y:0}{x:0,y:0}。这是因为ilength-1开始,而length在for循环上方设置为5。具体请参考以下内容:
[T][x][x][x][H][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]

接下来,在绘画函数中,它获取尾部的位置,因为它打算将尾部移动到头部即将到达的位置(通过这种方式,它不需要在下一次绘画之前移动任何其他单元格,因为蛇每个周期永远不会移动超过一个单元格。)

    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++;

蛇的尾巴总是在这个方案下数组的最后一个元素,头部总是第一个。因此,snake_array[snake_array.length]始终是尾部,snake_array[0]始终是头部。
记住{x:4,y:0},{x:3,y:0},{x:2:,y:0},{x:1,y:0},{x:0,y:0},并假设我们的默认移动方向是向右的,我们可以计算{x:5,ny:0}。现在,nx和ny代表的是蛇没有占据的单元格,但蛇想要移动到的单元格。因此,代码对该单元格进行边界和碰撞检查,以查看它是否与任何墙壁或其他蛇段相交,如果相交,则重新开始游戏。
它还检查食物碰撞并处理它(不是移动尾巴,而是在单元格位置创建一个新头部并生成另一块食物)。
但是,假设这个单元格(nx,ny)位置没有与任何东西碰撞,它会运行以下代码:
    var tail = snake_array.pop();
    tail.x = nx; tail.y = ny;

这个函数从snake_array中移除尾部,将其位置设置为我们之前计算出的单元格位置(nx,ny),然后将这个“尾巴”移到数组的第一个位置(根据我们之前的约定,这总是头部):

    snake_array.unshift(tail);

所以最终结果是这样的:
[ ][x][x][x][H][T]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]

但实际上,数组仍然是以 [H][x][x][x][T] 的形式存在的,因为我们决定将 snake_array[0] 中的第一个元素位置视为头部,将 snake_array[snake_array.length] 中的最后一个元素位置视为尾巴。 因此它实际上是这样的:

[ ][T][x][x][x][H]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]
[ ][ ][ ][ ][ ][ ]

然后整个过程可以一遍又一遍地重复,直到你赢或输!

请问您能否详细说明一下,如果有重叠的情况,那么画布上应该有3个蛇格子,而不是4个,因为两个格子将会在相同的x、y坐标位置上绘制。 - Ayo
实际上,我有点误读了代码,请给我一分钟来修正我的答案。 - jconder
是的 - 我编辑了我的答案,因为我误读了它(我以为它是在处理数组中最后一个项目的值)。我认为它可能是一个错误,因为它是这样做的(如果不是,我需要看更多的代码才能弄清楚原因)。 - jconder
好的,非常感谢您的回复。这是一个教程链接,也许您可以查看它以获取完整的要点,这样可以帮助您更好地解释它 http://thecodeplayer.com/walkthrough/html5-game-tutorial-make-a-snake-game-using-html5-canvas-jquery 谢谢 - Ayo
啊,经过审查,基本上是这样的,运动控制头部,但不是将头部呈现在其他东西的前面(这会导致我在分离段落答案中提到的问题),而是将尾巴移动到下一个头部位置。但它还检查这两种情况之间的碰撞,所以当发生碰撞时,它会重新开始。如果您认为有用,我可以使用这些信息重写答案。 - jconder
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接