用canvas写贪吃蛇

最近参加了baidu_ife_2017,里面有个任务是用canvas写贪吃蛇游戏,游戏有三种玩法,不过现在我也只是完成了前两种(普通模式和过关模式),想记录一下遇到的几个问题和解决方法,各位走过路过也可以给些建议。可以到此试玩一下:Snake Online

1.如何让蛇动起来

用canvas来写游戏,无非就是设置个定时器,不断清空画布然后重绘。而让蛇动起来,让其身体每一截都往前移动,这显然是非常麻烦的一件事,比如蛇身现在弯弯曲曲,而其每一截都要向爬行方向移动一格,还需要给每一截重新计算下一步的位置。在我看来是十分麻烦的一件事,于是思索一番,发现蛇每次只是移动一格,那么如果我只改变最后一截,让其位置变为爬行方向的下一格不就解决了吗。想到这,我用一个数组来维护每一截的位置对象:

function Snake(args) {
    this.bodyArr = [];
    // 每次移动蛇头x,y值改变量
    this.dx = 0;
    this.dy = 0;
    //...
}

Snake.prototype.setBodyArr = function () {
    var newX = this.bodyArr[0].x + this.dx;
    var newY = this.bodyArr[0].y + this.dy;
    // 移除最后一截
    var temp = this.bodyArr.pop();
    // 将下一次要移动的位置插入到位置数组中
    this.bodyArr.unshift({
        x: newX,
        y: newY
    });
    // ...
};

2.食物的随机位置如何设置

或许大家觉得这个很简单,不就是产生个随机数,然后避开蛇身的位置就完事了么。我也是这么想的,但是想动手时却犯难了,我觉得应该是可以优化的。毕竟每产生一次随机数,就要和蛇身每一截去比较一次,重叠了又要重新产生随机数再全部比较一次… 虽然废不了多少时间,但总觉得可以优化,于是在github上找别人的解决方案,发现很多人都是使用一个二维数组来维护地图上每个位置的状态的,觉得不错于是将其用上了:

function Snake(args) {
    // position[x][y] = 0 为空,
    // position[x][y] = 1 为蛇身,
    // position[x][y] = 2 为食物
    this.position = creat2dArr(width, height);

    //...
}
// 产生一个值全为0的二维数组
function creat2dArr(width, height) {
    var arr = new Array(width);

    for(var j = 0; j < width; j++) {
        arr[j] = new Array(height);
        for(var k = 0; k < height; k++) {
            arr[j][k] = 0;
        }
    }

    return arr;
}

每次只需比较 position[x][y] 的值即可知道该位置的状态。

3.蛇爬行方向的改变

游戏是通过方向键来控制的,那么如果玩家在两次重绘之间,按下了多次不同的方向键,那么蛇下一次爬行的方向该定为什么?怎么解决这个冲突?我想到的是用一个队列来保存玩家的输入,每次重绘前取出队列的首项,如果该方向合理(即不是与当前方向相同或相反),则修改下一步的方向,通过这种方式,玩家的每一次输入都可以得到响应,只是快与慢的问题罢了。

function Snake(args) {
    // 爬行方向("up": 上,"down": 下,"left": 左,"right": 右)
    this.directionQueue = []; 
    this.nextDirection = "up"; // 定义初始爬行方向为上

    //...
}

// 设置下一次爬行的方向
Snake.prototype.setNextDirection = function () {
    if(this.directionQueue.length === 0) { // 如果用户没有输入下一个方向则返回
        return;
    }

    var temp = "";

    while(this.directionQueue.length !== 0) {
        if((temp = this.directionQueue.shift()) !== this.nextDirection) {
            // 判断方向合法性
            if(this.judgeLegality(temp)) {
                this.nextDirection = temp;
                // 设置dx与dy的值
                if(temp === "up") {
                    this.dx = 0;
                    this.dy = -1;
                } else if(temp === "down") {
                    this.dx = 0;
                    this.dy = 1;
                } else if(temp === "left") {
                    this.dx = -1;
                    this.dy = 0;
                } else {
                    this.dx = 1;
                    this.dy = 0;
                }
                break;
            }
        }
    }
};

// 页面加载完成后
window.onload = function () {
    var snake = new Snake(args);
    // 注册键盘事件
    onEvent(document, "keydown", function (e) {
        e = e || window.event;

        if(e.keyCode === 37) { // left
            snake.directionQueue.push("left");
        } else if(e.keyCode === 38) { // up
            snake.directionQueue.push("up");
        } else if(e.keyCode === 39) { // right
            snake.directionQueue.push("right");
        } else if(e.keyCode === 40) { // down
            snake.directionQueue.push("down");
        }
    });
};

结语

这些是我做贪吃蛇时碰到的几个问题,写出来分享下。如果觉得太白痴了请略过… 如果觉得还可以改进,欢迎在下方留言。

IT文库 » 用canvas写贪吃蛇
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址