JS+Jquery:2048游戏

终于完成了2048小游戏的制作~
UI长这样
JS+Jquery:2048游戏_第1张图片
使用键盘的上下左右来控制数字块的活动。
下面主要是记录一下这个游戏在制作过程中遇到的一些问题。


UI的设置?如何确定每一个块的位置?

我们可以看到,其实每次移动都是块的移动或合并。所以这个过程是有一个动画的,就是块的横向或纵向地改变自己的坐标。
但是,我们看到的块的移动并不是
JS+Jquery:2048游戏_第2张图片这个背景块在移动,而是

JS+Jquery:2048游戏_第3张图片这个数字块。
就是说其实在背景块的上面还是悬浮着16个和背景块位置一样的数字块的。
但是转念一想,如果每次移动都改变数字块的位置,那下次移动岂不是有两个数字块会在同一位置?这样的话原本数字块的位置岂不是空着?怎么产生一个新的数字块?
因此在这里,需要设置一个函数updateGrid,这个函数的作用就是在每次移动完后对数字块进行重新分布,再对每个数字块的样式进行渲染。这样的话又是一个新的4x4的数字块了。


生成随机数字块和移动过程中的动画如何实现?

生成随机数字块动画

刚刚上面提到了那个函数updateGrid,会重新分配16个数字块的位置并改变它们的样式。
每个数字块在每一次移动中都是有值空着的,对有值的数字块进行渲染即可,空着的数字块则令其宽高为0,位置在其应该在的背景块的位置上的正中间
JS+Jquery:2048游戏_第4张图片如图所示。
为什么是正中间?
为了制作生成数字的动画。代码如下

function showAnimation(x, y, num) {
    var grid_num = $("#grid-num-"+x+"-"+y);
    grid_num.css("background-color", getBackgroundColor(num));
    grid_num.css("color", getColor(num));
    grid_num.text(num);
    grid_num.animate({
        width:"100px",
        height:"100px",
        left: y*120+20,
        top: x*120+20
    }, 100);
}

给这个生成数字块的函数传入三个参数,就可以通过其应该在的坐标推算出它所在的位置了,再通过变化宽和高从0到100就可以实现生成数字块动画了。

移动过程动画

在2048中,移动数字块只是横向或纵向地改变,即left或top的变化。所以只要获取到源坐标(用于确定那个数字块需要被改变)和目的坐标(用于确定改变后的left和top)即可。

function showMoveAnimation(fx, fy, tx, ty) {
    var grid_num = $("#grid-num-"+fx+"-"+fy);
    grid_num.animate({
        left: ty*120+20,
        top: tx*120+20
    }, 200);
}

如何实现上下左右移动数字块?逻辑是什么?

这里拿右移动为例。

判断可否移动

JS+Jquery:2048游戏_第5张图片
在右移时,最右边那一列肯定是没办法移动了。所以不需要考虑是否可以移动。
于是考虑前面三列,可以发现,可以移动的情况有两种:
1. 要移动的块后面紧邻有空着的位置。
2. 要移动的块后面紧邻和它数字一样的块。

JS+Jquery:2048游戏_第6张图片
4是不可以右移的,2满足
JS+Jquery:2048游戏_第7张图片
2可以往右移
一旦4x4的格子内有可以移动的块,那么它就是可以右移的。

移动 是合并还是单纯右移

这个就比较麻烦一点。我的想法是,遍历那些有数字的块,再对这些有数字的块循环查看是否存在可以合并的点或可以移动到该地方的点。
于是考虑前面三列每一个数字块,可以发现,可以移动的情况有两种:
1. 要移动的块后面有空着的位置。
2. 要移动的块后面有和它数字一样的块。
3. 要移动的块和后面空着的块或有相同数字的块之间没有其他块阻隔。
遍历的顺序很重要:
JS+Jquery:2048游戏_第8张图片
不用考虑黑色斜线划掉的块,那么需要先考虑图中红色框住的块4,并且先遍历那些比它大的列,就是图中的红“1”,发现不满足1,2,所以无法移动。
再考虑下一个
JS+Jquery:2048游戏_第9张图片
图中被框住的节点优先考虑的是图中的红“1”,但是发现其满足2但不满足3,因此无法合并
再考虑红“2”,不满足2,pass。
考虑红“3”,满足1和3,可以右移!

合并的问题

合并有一个很大的问题就是,若遇到了下图并左移,结果应该是【4,4,0,0】而不是【8,0,0,0】
JS+Jquery:2048游戏_第10张图片
依照我们上述的思想,第一应该是先变成【4,0,0,4】,第二步就会变成【8,0,0,0】了!
所以应该对每一个块的合并都有一个记录,一旦两个块合并过了,下次就不能再进行合并。
这里维护了一个conflicted数组,主要是记录一旦合并就记录合并的坐标的值为true,下次合并必须满足合并的坐标的conflicted的值为false。

代码如下:

function moveRight() {
    if(canMoveRight(board)) {
        for(var i = 0; i < 4;i++) {
            for (var j = 2; j >= 0; j--) {
                if(board[i][j]!=0) {
                    for(var k = 3; k > j; k--) {
                        if(board[i][k] == 0 && noBlockHorizontal(i, k, j)) {
                            score+=board[i][j];
                            board[i][k] = board[i][j];
                            board[i][j] = 0;
                            showMoveAnimation(i, j, i, k);
                        }
                        if(board[i][j] == board[i][k] && noBlockHorizontal(i, k, j) && !conflicted[i][k]) {
                            score+=board[i][j];
                            board[i][k] += board[i][j];
                            board[i][j] = 0;
                            conflicted[i][k] = true;
                            showMoveAnimation(i, j, i, k);
                        }
                    }
                }
            }
        }
        setTimeout("updateGrid()", 200);
        return true;
    } else {
        return false;
    }
}

更新网格

提到updateGrid说在块移动动画完成的时候更新网格位置,所以这里有个很重要的地方就是setTimeout函数,使用它来延迟updateGrid的开始执行时间,保证其在移动动画执行完后再执行。
生成新的块也要在移动动画完成后再生成,所以也需要用到setTimeout函数。


游戏结束

这里最坑的就是游戏结束了==
就是因为setTimout函数导致的时延。

//按向右键
case 39:
    if(moveRight()) {
        setTimeout("getOneNumberRandomly()", 200);
        isGameover();
    }
    break;

发现什么奇怪的地方了吗?isGameover()执行将快过getOneNumberRandomly()!
即还没有生成新的块它就判断游戏是否结束了…
改为下面这句就对了,延迟执行时间

setTimeout("isGameover()", 300);

结语

终于写完了。
还是学到不少数学逻辑的。

你可能感兴趣的:(JS+Jquery:2048游戏)