• jQuery实践-网页版2048小游戏


    ▓▓▓▓▓▓ 大致介绍

      看了一个实现网页版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>
    View Code

      

      样式:

     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 }
    View Code
    1 // 获取位置
    2 function getPos(num){
    3     return 20 + num*120;
    4 }
    View Code

      这样我们的第一层就好了

      效果:

      现在构造第二层,即构建一个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                     '0px',
    12                     height:'0px',
    13                     top:getPos(i) + 50,
    14                     left:getPos(j) + 50
    15                 })
    16             }else{
    17                 everyNumber.css({
    18                     '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 }
    View Code
     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 }
    View Code
    1     // 设置相应数字的文字颜色
    2 function getColor(number){
    3     if (number <= 4) {
    4         return "#776e65"
    5     }
    6     return "white";
    7 }
    View Code

    ▓▓▓▓▓▓ 初始化

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

      代码:

     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 }
    View Code
     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                  '100px',
    11                  height:'100px',
    12                  top:getPos(randomX),
    13                  left:getPos(randomY)
    14              },50);
    15 }
    View Code

    ▓▓▓▓▓▓ 基本操作

      我们通过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 });
    View Code

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

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

        第一种:当前格子的左边的格子是空的即值为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 }
    View Code

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

      代码:

     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 }
    View Code
    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 }
    View Code

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

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

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

      代码:

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

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

      代码:

    1 // 判断游戏是否结束
    2 function wheGameOver(checkerboard){
    3     if(!canMoveLeft(checkerboard) && !canMoveUp(checkerboard) && !canMoveRight(checkerboard) && !canMoveDown(checkerboard) ){
    4         showGameOver();
    5     }
    6 }
    View Code
     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 }
    View Code

    ▓▓▓▓▓▓ 最后优化

      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;
    View Code

      从代码中可以看出,判断游戏是否结束是在随机产生一个数字前执行的,所以在判断游戏结束时,总是有一个空的格子,所以代码执行后认为游戏没有结束,但是当这个随机数字产生后,所有的格子不能移动,当我们按键时,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);  
    View Code

    ▓▓▓▓▓▓ 总结

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

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

     

  • 相关阅读:
    onpropertychange与onchange事件应用
    HttpWorkerRequest实现大文件上传asp.net
    JQuery中对option的添加、删除、取值
    "分析 EntityName 时出错"的解决方法
    asp.net断点续传
    直接在ASP.net中上传大文件的方法
    ASP.NET中文件上传下载方法集合
    asp.net .ashx文件使用Server.MapPath解决方法
    FF与IE下javascript计算屏幕尺寸
    处理顶点——为赛道创建顶点
  • 原文地址:https://www.cnblogs.com/qqandfqr/p/6254193.html
Copyright © 2020-2023  润新知