用canvas写贪吃蛇

来源:互联网 发布:南威软件林志强 编辑:程序博客网 时间:2024/05/19 02:26

最近参加了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");        }    });};

结语

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

0 0