jQuery实践-网页版2048小游戏

2017-01-06 07:56:40来源:cnblogs.com作者:老板丶鱼丸粗面人点击

▓▓▓▓▓▓ 大致介绍

  看了一个实现网页版2048小游戏的视频,觉得能做出自己以前喜欢玩的小游戏很有意思便自己动手试了试,真正的验证了这句话-不要以为你以为的就是你以为的,看视频时觉得看懂了,会写了,但是自己实现起来会遇到各种问题。比如,在最后判断游戏是否结束的时候,我写的语句语法是对的,但就是不执行。最后通过对视频源码的分析对比,发现原作者写的一个setTimeout定时器有额外的意思,本来我以为它就是简单的一个延时动画,其实他是在等待另外一个函数执行完毕。-_-||。最后还是很高兴能写出来,也改进了一些源代码的不足。

  jQuery在这个游戏中的应用并不多,如果对其中的jQuery语法有疑问,可以参考我写的jQuery学习之路(持续更新),里面有讲解

  预览:2048网页版

  这篇博客并不是详细的讲解,只是大致介绍函数的作用,其中实现的细节注释中有解释,网上的这个源码有点乱,如果想看比较整齐的源码或者视频的可以QQ联系我(免费)(找共同学习的伙伴)

 

 

▓▓▓▓▓▓ 思路

  这个小游戏可以抽象化分为3层(我觉得这样能更好理解)

    ◆最底下的一层是基本的样式(可见的)

    ◆中间的层是最主要的,是一个4x4的二维数组,游戏中我们都是对这个二维数组进行操作(不可见的)

    ◆最上面的一层也是一个4x4的二维数组,它只是根据第二层数组的每个数显示样式(可见的)

  我们通过最底下的一层显示最基本的16个小方格,通过键盘的按键或者手指在屏幕的滑动来操作中间层的数组,最后在通过最上面的一层显示出数字

 

▓▓▓▓▓▓ 基本结构与样式

  基本的结构和样式都挺简单,直接看代码

  结构:

 1 <div id="test2048"> 2         <div id="header"> 3             <h1>2048</h1> 4             <a href="javascript:newgame()" >开始新的游戏</a> 5             <p>分数:<span id="score">0</span></p> 6         </div> 7         <div id="container"> 8             <div class="cell" id="cell-0-0"></div> 9             <div class="cell" id="cell-0-1"></div>10             <div class="cell" id="cell-0-2"></div>11             <div class="cell" id="cell-0-3"></div>12             <div class="cell" id="cell-1-0"></div>13             <div class="cell" id="cell-1-1"></div>14             <div class="cell" id="cell-1-2"></div>15             <div class="cell" id="cell-1-3"></div>16             <div class="cell" id="cell-2-0"></div>17             <div class="cell" id="cell-2-1"></div>18             <div class="cell" id="cell-2-2"></div>19             <div class="cell" id="cell-2-3"></div>20             <div class="cell" id="cell-3-0"></div>21             <div class="cell" id="cell-3-1"></div>22             <div class="cell" id="cell-3-2"></div>23             <div class="cell" id="cell-3-3"></div>24         </div>25     </div>

  

  样式:

 1 *{ 2     margin: 0; 3     padding: 0; 4 } 5 #test2048{ 6     font-family: Arial; 7     margin: 0 auto; 8     text-align: center; 9 }10 #header{11     margin: 20px;12 }13 #header a{14     font-family: Arial;15     text-decoration: none;16     display: block;17     color: white;18     margin: 20px auto;19     width: 125px;20     height: 35px;21     text-align: center;22     line-height: 40px;23     background-color: #8f7a66;24     border-radius: 10px;25     font-size: 15px;26 }27 #header p{28     font-family: Arial;29     font-size: 20px;30 }31 #container{32     width: 460px;33     height: 460px;34     background-color: #bbada0;35     margin: 0 auto;36     border-radius: 10px;37     position: relative;38     padding: 20px;39 }40 .cell{41     width: 100px;42     height: 100px;43     border-radius: 6px;44     background-color: #ccc0b3;45     position: absolute;46 }
CSS样式

 

  从CSS样式可以看出,我们并没有对每个格子的位置进行设置,因为如果用CSS给每个格子设置样式代码量太大,而且他们的位置有一定的规律,所以我们可以用js循环来完成每个格子样式的设置

  代码:

 1 // 初始化棋盘格 2 function initialize(){ 3     for(var i=0;i<4;i++){ 4         for(var j=0;j<4;j++){ 5             // 设置棋盘格的位置 6             var everyCell = $('#cell-'+ i +'-'+ j); 7             everyCell.css({top:getPos(i),left:getPos(j)}); 8         } 9     }10 }
1 // 获取位置2 function getPos(num){3     return 20 + num*120;4 }

 

  这样我们的第一层就好了

  效果:

 

  现在构造第二层,即构建一个4x4的值全部为0的数组,由于在构造第二层时,有两层循环,所以我们可以在构造第一层时也能构造第二层

  第三层是用js生成16个格子,它和第一层的16个格子一一对应

  代码:

 1 // 数字格 2 function numFormat(){ 3     for(var i=0;i<4;i++){ 4         for(var j=0;j<4;j++){ 5             $('#container').append('<div class="number" id="number-'+ i +'-'+ j +'"></div>') 6  7             // 设置数字格的位置,样式 8             var everyNumber = $('#number-'+ i +'-'+ j); 9             if(checkerboard[i][j] == 0){10                 everyNumber.css({11                     width:'0px',12                     height:'opx',13                     top:getPos(i) + 50,14                     left:getPos(j) + 5015                 })16             }else{17                 everyNumber.css({18                     width:'100px',19                     height:'100px',20                     top:getPos(i),21                     left:getPos(j),22                     backgroundColor:getBackgroundColor(checkerboard[i][j]),23                     color:getColor(checkerboard[i][j])24                 });25                 everyNumber.text(checkerboard[i][j]);26             }27         }28     }29 }
 1 // 获取相应数字的背景颜色 2 function getBackgroundColor(number){ 3  4     switch (number) { 5         case 2:return "#eee4da";break; 6         case 4:return "#ede0c8";break; 7         case 8:return "#f2b179";break; 8         case 16:return "#f59563";break; 9         case 32:return "#f67c5f";break;10         case 64:return "#f65e3b";break;11         case 128:return "#edcf72";break;12         case 256:return "#edcc61";break;13         case 512:return "#9c0";break;14         case 1024:return "#33b5e5";break;15         case 2048:return "#09c";break;16         case 4096:return "#a6c";break;17         case 8192:return "#93c";break;18     }19 }
1     // 设置相应数字的文字颜色2 function getColor(number){3     if (number <= 4) {4         return "#776e65"5     }6     return "white";7 }

 

▓▓▓▓▓▓ 初始化

  在每次游戏重新开始时,都会在随机的位置出现两个随机的数字,我们写一个在随机位置出现一个随机数的函数,只要调用两次就可以实现了

  代码:

 1 // 随机的在一个位置上产生一个数字 2 function randomNum(){ 3     // 随机产生一个坐标值 4     var randomX = Math.floor(Math.random() * 4); 5     var randomY = Math.floor(Math.random() * 4); 6  7     // 随机产生一个数字(2或4) 8     var randomValue = Math.random() > 0.5 ? 2 : 4; 9 10     // 在数字格不为0的地方生成一个随机数字11     while(true){12         if(checkerboard[randomX][randomY] == 0){13             break;14         }else{15 16             var randomX = Math.floor(Math.random() * 4);17             var randomY = Math.floor(Math.random() * 4);18         }19     }20 21     // 将随机产生的数字显示在随机的位置上22     checkerboard[randomX][randomY] = randomValue;23 24     // 动画25     randomNumAnimate(randomX,randomY,randomValue);26 }
 1 // 随机产生数字的动画 2 function randomNumAnimate(randomX,randomY,randomValue){ 3     var randomnum = $('#number-'+ randomX +'-'+ randomY); 4     randomnum.css({ 5         backgroundColor:getBackgroundColor(randomValue), 6         color:getColor(randomValue), 7     }) 8              .text(randomValue) 9              .animate({10                  width:'100px',11                  height:'100px',12                  top:getPos(randomX),13                  left:getPos(randomY)14              },50);15 }

 

▓▓▓▓▓▓ 基本操作

  我们通过switch循环,来根据用户不同的输入进行不同的操作

  代码:

 1 // 获取键盘事件,检测不同的按键进行不同的操作 2 $(document).keydown(function(event){ 3     switch(event.keyCode){ 4         case 37:// 5             if(canMoveLeft(checkerboard)){ 6                 // 如果可以向左移动 7  8                 MoveLeft(); 9                 // 向左移动10 11 12                 setTimeout(function(){13                     randomNum();14                 },200);15                 // 随机产生一个数字16             }17             break;18         case 38://19             if(canMoveUp(checkerboard)){20                 // 如果可以向上移动21 22                 MoveUp();23                 // 向上移动24 25                 setTimeout(function(){26                     randomNum();27                 },200);28                 // 随机产生一个数字29             }30             break;31         case 39://32             if(canMoveRight(checkerboard)){33                 // 如果可以向右移动34 35                 MoveRight();36                 // 向右移动37 38                 setTimeout(function(){39                     randomNum();40                 },200);41                 // 随机产生一个数字42             }43             break;44         case 40://45             if(canMoveDown(checkerboard)){46                 // 如果可以向下移动47 48                 MoveDown();49                 // 向下移动50 51                 setTimeout(function(){52                     randomNum();53                 },200);54                 // 随机产生一个数字55             }56             break;57         default:58             break;59     }60 });

 

  由于数字格的移动只有左、上、右、下四种方式,并且他们都是大同小异的,所以就拿向左移动为例,

  向左移动,我们首先需要判断它是否能向左移动,能向左移动有两种情况

    第一种:当前格子的左边的格子是空的即值为0

    第二种:当前格子的值和左边格子的值相同

  由于向左移动,所以第一列的格子不可能向左移动,所以不需要判断

  代码:

 1 // 判断是否可以向左移动 2 function canMoveLeft(checkerboard){ 3     for(var i=0;i<4;i++){ 4         for(var j=1;j<4;j++){ 5             if(checkerboard[i][j] != 0){ 6                 // 如果这个数字格它左边的数字格为空或者左边的数字格和它相等,则可以向左移动 7                 if(checkerboard[i][j-1] == 0 || checkerboard[i][j] == checkerboard[i][j-1]){ 8                     return true; 9                 }10             }11         }12     }13     return false;14 }

 

  判断能否向左移动后,我们就要对可以移动的格子进行移动,这里需要特别注意,向哪个方向移动就要先从哪个方向开始判断

  代码:

 1 // 向左移动 2 function MoveLeft(){ 3     for(var i=0;i<4;i++){ 4         for(var j=1;j<4;j++){ 5             if(checkerboard[i][j] != 0){ 6                 for(var k=0;k<j;k++){ 7                     if(checkerboard[i][k] == 0 && noMiddleNumRow(i,k,j,checkerboard)){ 8                         moveAnimation(i,j,i,k); 9                         checkerboard[i][k] = checkerboard[i][j];10                         checkerboard[i][j] = 0;11                     }else if(checkerboard[i][k] == checkerboard[i][j] && noMiddleNumRow(i,k,j,checkerboard) && !hasConflicted[i][k]){12                         moveAnimation(i,j,i,k);13                         checkerboard[i][k] += checkerboard[i][j];14                         checkerboard[i][j] = 0;15 16                     }17                 }18             }19         }20     }21     // 设置刷新的时间是为了让运动的动画走完在进行更新数字格,否则数字格运动的动画将会被打断22     setTimeout(function(){23         numFormat();24     },200);25 }
1 // 判断中间的数字格是否为0(行)2 function noMiddleNumRow(row,col1,col2,checkerboard){3     for(var i=col1+1;i<col2;i++){4         if(checkerboard[row][i] != 0){5             return false;6         }7     }8     return true;9 }

 

  将上、右、下四个方向写完以后,游戏基本的操作就已经完成了。

 

▓▓▓▓▓▓ 游戏分数和判断游戏结束

  游戏的分数是每个相加的数的和,所以我们在每个数相加的时候更新分数就可以了

  代码:

1                         // 更新分数2                         score += checkerboard[k][j];3                         updateScore(score);
1 // 设置分数2 function updateScore(num){3     $('#score').text(num);4 }

 

  判断游戏是否结束很简单,用我们之前定义的方法就可以实现

  代码:

1 // 判断游戏是否结束2 function wheGameOver(checkerboard){3     if(!canMoveLeft(checkerboard) && !canMoveUp(checkerboard) && !canMoveRight(checkerboard) && !canMoveDown(checkerboard) ){4         showGameOver();5     }6 }
 1 // 显示游戏结束 2 function showGameOver(){ 3     $('#container').append("<div id='gameover'><p>最终得分</p><span>"+ score +"</span><a href='javascript:resert();'>重新开始游戏</a></div> ") 4 } 5  6 // 重新开始游戏 7 function resert(){ 8     $('#gameover').remove(); 9     newgame();10 }

 

▓▓▓▓▓▓ 最后优化

  1、游戏中会出现一次移动,一个数会被累加很多次

  在原游戏中,每个数在每次操作中只能累加一次,所以我们在定义一个4x4的值为false的数组,与中间层的数组一一对应,专门用来防止一个数的多次累加,如果是false则可以累加,并将值改为false,否则不可以累加

  2、结束死循环

  由于在设置随机数的时候用到了一个死循环,但是在游戏结束后,该循环还在,所以我们在死循环中在添加一个条件,如果游戏结束就跳出循环

  3、最后的结束游戏提示不执行

 

 1         case 37:// 2             if(canMoveLeft(checkerboard)){ 3                 // 如果可以向左移动 4  5                 MoveLeft(); 6                 // 向左移动 7  8                 setTimeout(function(){ 9                     wheGameOver(checkerboard)10                 },300);11                 // 判断游戏是否结束,这里设置延时是因为要等到随机产生数字后再进行判断,如果不加12                 // 延时,则最后一次的判断因为canMoveLeft(checkerboard)为false就不会再执行了13 14                 setTimeout(function(){15                     randomNum();16                 },200);17                 // 随机产生一个数字18             }19             break;

  从代码中可以看出,判断游戏是否结束是在随机产生一个数字前执行的,所以在判断游戏结束时,总是有一个空的格子,所以代码执行后认为游戏没有结束,但是当这个随机数字产生后,所有的格子不能移动,当我们按键时,if条件不通过,判断游戏是否结束的函数不能执行。所以我们要给判断游戏结束的函数设置定时器,让他在随机产生一个数字后再进行判断

  4、在移动端可以执行

  由于原作者没有写有关移动端的操作,所以我在网上找的判断移动端触屏手机滑动位置的代码,加入了游戏的事件就可以执行了

  1         //返回角度    2          function GetSlideAngle(dx, dy) {    3              return Math.atan2(dy, dx) * 180 / Math.PI;    4          }    5     6          //根据起点和终点返回方向 1:向上,2:向下,3:向左,4:向右,0:未滑动    7          function GetSlideDirection(startX, startY, endX, endY) {    8              var dy = startY - endY;    9              var dx = endX - startX;   10              varresult = 0;   11    12              //如果滑动距离太短   13              if(Math.abs(dx) < 2 && Math.abs(dy) < 2) {   14                  returnresult;   15              }   16    17              var angle = GetSlideAngle(dx, dy);   18              if(angle >= -45 && angle < 45) {   19                  result = 4;   20              }else if (angle >= 45 && angle < 135) {   21                  result = 1;   22              }else if (angle >= -135 && angle < -45) {   23                  result = 2;   24              }   25              else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {   26                  result = 3;   27              }   28    29              return result;   30          }   31    32          //滑动处理   33          var startX, startY;   34          document.addEventListener('touchstart',function (ev) {   35              startX = ev.touches[0].pageX;   36              startY = ev.touches[0].pageY;     37          }, false);   38          document.addEventListener('touchend',function (ev) {   39              var endX, endY;   40              endX = ev.changedTouches[0].pageX;   41              endY = ev.changedTouches[0].pageY;   42              var direction = GetSlideDirection(startX, startY, endX, endY);   43              switch(direction) {   44                  case 0:   45                       //没滑动  46                      break;   47                  case 1:   48                      if(canMoveUp(checkerboard)){ 49                         // 如果可以向上移动 50  51                         MoveUp(); 52                         // 向上移动 53  54                         setTimeout(function(){ 55                             wheGameOver(checkerboard) 56                         },300); 57                         // 判断游戏是否结束 58  59                         setTimeout(function(){ 60                             randomNum(); 61                         },200); 62                         // 随机产生一个数字 63                      }  64                      break;   65                  case 2:   66                      if(canMoveDown(checkerboard)){ 67                         // 如果可以向下移动 68  69                         MoveDown(); 70                         // 向下移动 71  72                         setTimeout(function(){ 73                             wheGameOver(checkerboard) 74                         },300); 75                         // 判断游戏是否结束 76  77                         setTimeout(function(){ 78                             randomNum(); 79                         },200); 80                         // 随机产生一个数字 81                      }   82                      break;   83                  case 3:   84                      if(canMoveLeft(checkerboard)){ 85                         // 如果可以向左移动 86  87                         MoveLeft(); 88                         // 向左移动 89  90                         setTimeout(function(){ 91                             wheGameOver(checkerboard) 92                         },300); 93                         // 判断游戏是否结束,这里设置延时是因为要等到随机产生数字后再进行判断,如果不加 94                         // 延时,则最后一次的判断因为canMoveLeft(checkerboard)为false就不会再执行了 95  96                         setTimeout(function(){ 97                             randomNum(); 98                         },200); 99                         // 随机产生一个数字100                      }   101                      break;  102                  case 4:  103                      if(canMoveRight(checkerboard)){104                         // 如果可以向右移动105 106                         MoveRight();107                         // 向右移动108 109                         setTimeout(function(){110                             wheGameOver(checkerboard)111                         },300);112                         // 判断游戏是否结束113 114                         setTimeout(function(){115                             randomNum();116                         },200);117                         // 随机产生一个数字118                      }  119                      break;  120                  default:             121              }  122          }, false);  

 

▓▓▓▓▓▓ 总结

  总体来说这个游戏实现起来并不是太难,就是许多小的操作集合起来

  如果想看视频或者源码请QQ联系我

 

 

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台