Html部分(界面):1.开始新游戏;2.返回上一步;3.记分栏; 4.16个小格组成;
其中1,2由链接形式实现.
a标签中href属性调用js方法;
<a href="javascript:new_game();"id="new_game_button">开始新游戏</a>
CSS部分:
1.字体采用@media方法适应屏幕大小;
2.背景待修改;(17行);
3.各元素的宽度、高度等单位为rem;默认1rem等于多少像素??
4.html中设置的16个小格作为背景不动,每个小格(grid_cell,其仅作背景)包含一个相同大小的小格(number_cell,其包含数字),若有数字要显示则对它设置宽、高,没有数字则其默认宽、高为0不显示。
Js部分
适配移动端:屏幕可用宽度小于一定程度时,设置字体大小;
开始新游戏:
1.初始化16个作为背景的小格子,它们的宽、高在css中已设置,在js中只需通过两层循环嵌套,对其定位即可(jQuery的css方法设置top、left值);
2.再次通过两层循环初始化每个格子的数字为0,即默认值为0;
3.更新棋局
3.1通过两层for循环嵌套,将16个number_cell添加到#grid_container中;
3.2判断number_cell中的数字大小;
3.2.1 number_cell中的数字大小为默认值0时,设置宽、高为0,相对于不显示数字;
3.2.2number_cell中的数字大于1024时(4位数),设置其宽、高与grid_cell相同,top和left也与grid_cell相同,即它们重叠了,只是看不出来,再修改小格子背景颜色和字体的颜色及大小;(都包含在number_cell.css()方法中),数字小于4为灰色,大于4后为白色,背景颜色通过switch,分别对2的n次方(2、4、8、16、32、64、128、256、512、1024、2048)进行设置,由于1024由4个数字组成,要使格子能将其显示完整,应该减小字体的大小,通过number_cell.text(board[i][j]);将数字得以显示;
3.2.3 number_cell中的数字大于100小于1024时(三位数),基本同上,改变位置,改变背景颜色、字体颜色、字体大小(相比于4位稍大,相比两位稍小);
3.2.4 number_cell中的数字小于100小于时(两位数),同上;
4.初始化分数score为0,更新分数,即把分数显示(text()方法);
5.界面初始化完成后,随机生成两个数字到两个格子中;generate_one_number,要生成数字放入格子,首先得判断是否还有空的格子,若16个格子全被占了,则直接返回,而判断是否还有空格子,就是遍历16个格子,如果格子中的数字还为0,则说明该格子可用。接下来确定随机位置,结合Math.floor()和Math.random()确定坐标(0,1,2,3),设定循环极限是50次,即在这50次中是随机确定位置,但是确定的位置不一定能用(可能已被数字占用),若位置能用(该位置数字为0),则成功;若位置不能用(数字非0),则继续调用随机方法,次数加1,直到次数等于50次,此时若还未找到可用位置,则不采用随机方式,而采用遍历16个格子的方法,依次遍历,找数字为0的格子,找到后,记录其坐标;接下来确定随机数字,由于只希望初始时出现2或4,我们采用Math.random() < 0.5将两种情况的概率划分为50%。为了有更好的体验,我们希望数字格子的出现具有一定的动画,使用number_cell.animate()方法结合数字格子宽、高、top、left值设置动画时间50ns(数字格子出现动画);由于我们有返回上一步这个功能,所以每生产一个数字格子,我们应及时保存当前分数和所有格子(遍历16个格子)对应的数字(o[n] = score+ ',' + board[i][j];o是一个对象,最后将o[1],o[2]…o[16]push到back数组中),保存到一个back数组中(数组中的每个值对应的是一个o对象,能取到那个时候所有格子的数字和那个时候的score)。
6.返回上一步功能的实现
<a href="javascript:come_back();" id="come_back_button">返回上一步</a>(红色部分执行js中的相应函数)
如果back数组成员小于或等于2,此时撤回不作任何效果,因为只有最开始的两个数字格子被记录在了back数组中,若back数组中成员个数大于2,取出back.length-2处的对象(该对象包含了上一步16个格子的数字及分数),遍历该对象,取出每个格子对应的board[i][j],这里以逗号为分隔,先找到逗号的索引,然后加1取后半部分o[n].substr(o[n].lastIndexOf(',')+1),而分数是逗号的前半部分,利用parseInt(o[n])可以直接取到,获取了每个格子的数字和分数,更新面板和分数,并back.pop(),它的作用就相当于一个指针,每次pop掉一个,相当于往回退一格,而我们更新面板时对应的数据不由它影响,因为那是一个对象保存着,由back.length-2每次退2步(收回两个数字格子)取得。
7.监听键盘的上下左右$(document).keydown(function(event){})
事件监听用的是keydown,结合switch针对上下左右(键码分别为37、38、39、40),按下它们中的任意一个,执行的操作都基本相同,首先阻止默认事件的发生event.preventDefault(),
其次判断能否上(下、左、右)移,如果可以移动,则随机产生一个数字格子,再判断游戏是否结束,随机产生格子先执行,判断游戏是否结束后执行,采用setTimeout的方式为它们设置不同用的时间来实现。
7.1判断能否上(下、左、右)移动的实现
7.1.1首先整体判断一次,即遍历12个格子(有4个不用遍历,因为就在边缘),看是否有能移动的,如果全部12个格子都不能移动返回false,直接结束,如果有能移动的(直接返回true)才继续执行。能否向某个方向移动,结合该游戏特点,当移动方向上有空格(数字为0的格子)或相邻的两个格子数字相同(可合并)。
7.1.2该方向上如果有可移动的格子,才具体分析,遍历12个格子,找其中数字不为0的格子,这里以左移为例,board[i][j] != 0,再依次遍历这个位置的左边所有格子,for (var k = 0; k < j; k++),此时又要分两种情况,一种是只移动,但是数字格子不能合并,另一种是不仅要移动,数字格子还能合并。
7.1.2.1只移动,数字格子不能合并
依次遍历这个位置的左边所有格子,且从最左边(保证能移的最远)开始找数字为0的格子,如果数字为0的格子和待移动的格子(待移动的格子可能不止一个,这个由循环遍历实现)之间的格子都为0或它们本身就相邻(判断之间的格子由no_block_horizontal实现,水平方向,这个过程很好实现,反向思维,只要找到不为0的格子返回false即可);符合移动条件后,执行移动动画,通过待移动的格子坐标确定jQuery元素,再使用animate方法指定top、left;最后把格子上的数字进行变动,待移动的格子数字赋给目标格子,并将自己清0;整个过程不影响score。
7.1.2.2要移动,数字格子能合并
判断board[i][k] == board[i][j] && no_block_horizontal(i, k, j, board) && !has_conflicted[i][k],其中has_conflicted[i][k]初始都是设定为false的,找到两个数字相同的格子且之间没有格子都为0时,执行移动格子的动画,进行数字变动(此时要将两个格子的数字相加),在对score进行赋值并更新,score每次增加的分数等于两个格子数字之和,最后将has_conflicted[i][k] = true,避免一次按键多次合并,最后更新面板。
有可能在执行7.1.2.2时其中有个格子已经先执行了7.1.2.1了,比如(1,1)和(1,3)的数字相等,而(1,1)的左边(1,0)是空格子,遍历过程中会先找到(1,1)执行7.1.2.1将(1,1)移动到(1,0),之后再遍历到(1,3),此时的(1,3)是与(1,0)有相同的数字,所以最终是合并到了最左边。
7.2判断游戏是否结束,游戏结束有两种可能
情况一:移动之后遍历16个格子,判断是否有格子达到2048,若有,则在分数栏显示success;
情况二:棋盘上没有空格子且上下左右均不能移动(没有空格子且没有相同数字格子),显示走投无路
8 适配移动端(怎么测试??)
监听移动设备的触摸开始、触摸移动、触摸结束
document.addEventListener('touchstart',function (event) {})
document.addEventListener('touchmove',function (evnet) {})
document.addEventListener('touchend',function (event){})
startx = event.touches[0].pageX;
endx = event.changedTouches[0].pageX;
var x = endx - startx;
x小于一定程度时不起作用;
水平和垂直还得比较Math.abs(x) > Math.abs(y)
还得判断x > 0还是x<0确定是向左还是向右