• 2048游戏详解


      由于最近在百度IFE看到有2048任务,所以昨天兴趣一来自己也做了一个。大概花了五个小时完成,不过不足之处是操作时没有滑动效果。昨晚新增了手机版本,流畅度还可以,不过由于没有滑动,游戏过程显得很突兀啊,且容我下次再加上吧。下面先讲讲这个游戏怎么实现,这是个人想的方法。不足之处,多多指教啊。

      首先我在做这个小游戏的时候我想到了两种方法:第一种方法就是本例用到的方法,利用方向键操作,只改变相应DIV块的背景以及更改文字,其特点是16个DIV的位置是固定不变的;第二种方法就是通过定位来实现,操作方向键/滑动屏幕时改变left/top值,这种方法的好处是更容易做滑动效果,不过需要多建一个DIV层或者加背景。时间关系,目前我只用了第一种。

      1、界面与样式

        PC端:HTML内容很简单,直接使用两个DIV包裹16个DIV即可;而CSS的话wrap及其它DIV都可以使用固定值,放数字的DIV先写统一的样式,每个数字DIV都预写一种特定class的样式。最后再加一个动画,就是2出来后的放大效果。为了不影响布局,我采用的是CSS3的transform:scale3d动画,而不是通过改变大小来实现动画。

        移动端:跟PC端的相比,这个的HTML页面只是比上面的增加了一张img背景图;而CSS方面则有较大差异,全部采用百分比布局,高度由背景图1:1撑开,由于高度不确定,加载完页面后需要使用js获取小DIV的宽度,并赋给DIV一个相同的行高,以使文字垂直居中

        HTML代码

     1 <!DOCTYPE html>
     2 <html>
     3     <head>
     4         <meta charset="utf-8" />
     5         <title>2048游戏</title>
     6         <link rel="stylesheet" type="text/css" href="css/main.css"/>
     7         <script type="text/javascript" src="js/main.js"></script>
     8     </head>
     9     <body>
    10         <div id="wrap">
    11             <div id="inner">
    12                 <div></div>
    13                 <div></div>
    14                 <div></div>
    15                 <div></div>
    16                 
    17                 <div></div>
    18                 <div></div>
    19                 <div></div>
    20                 <div></div>
    21                 
    22                 <div></div>
    23                 <div></div>
    24                 <div></div>
    25                 <div></div>
    26                 
    27                 <div></div>
    28                 <div></div>
    29                 <div></div>
    30                 <div></div>
    31             </div>
    32         </div>
    33     </body>
    34 </html>
    PC端
     1 <!DOCTYPE html>
     2 <html>
     3     <head>
     4         <meta charset="utf-8" />
     5         <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
     6         <title>2048游戏</title>
     7         <link rel="stylesheet" type="text/css" href="css/main.css"/>
     8         <script src="js/touch.js" type="text/javascript" charset="utf-8"></script>
     9     </head>
    10     <body>
    11         <div id="wrap">
    12             <!--背景图-->
    13             <div id="bgPic">
    14                 <img src="img/bg2.png"/>
    15             </div>
    16             <div id="inner">
    17                 <div></div>
    18                 <div></div>
    19                 <div></div>
    20                 <div></div>
    21                 
    22                 <div></div>
    23                 <div></div>
    24                 <div></div>
    25                 <div></div>
    26                 
    27                 <div></div>
    28                 <div></div>
    29                 <div></div>
    30                 <div></div>
    31                 
    32                 <div></div>
    33                 <div></div>
    34                 <div></div>
    35                 <div></div>
    36             </div>
    37         </div>
    38         <div id="text">提示:通过滑动屏幕操作游戏。</div>
    39         <script src="js/main.js" type="text/javascript" charset="utf-8"></script>
    40         <script type="text/javascript">
    41             var inner = document.getElementById("inner");
    42             var divs = inner.children;
    43             var divW = divs[0].offsetWidth;
    44             for(var i=0;i< 16;i++){
    45                 divs[i].style.lineHeight = divW + "px";
    46             }
    47         </script>
    48     </body>
    49 </html>
    移动端

        CSS代码

      1 @charset "utf-8";
      2 
      3 body,h1,h2,h3,h4,p,dl,dd,ul,ol,form,input,textarea,th,td,select{margin: 0;padding: 0;}
      4             em{font-style: normal;}
      5             li{list-style: none;}
      6             a{text-decoration: none;}
      7             img{border: none;vertical-align: top;margin: 0;}
      8             table{border-collapse: collapse;}
      9             input,textarea{outline: none;}
     10             textarea{resize:none;overflow: auto;}
     11             body{font-size:12px;font-family: arial;}
     12             
     13 #wrap{
     14     width: 492px;
     15     height: 492px;
     16     margin: 30px auto;
     17     background: #b8af9e;
     18     border-radius: 10px;
     19     padding: 5px;
     20 }
     21 #inner{
     22     width: 480px;
     23     height: 480px;
     24     overflow: hidden;
     25 }
     26 #inner div{
     27     width: 106px;
     28     height: 106px;
     29     margin-left: 14px;
     30     margin-top: 14px;
     31     background: #ccc0b2;
     32     float: left;
     33     font-size: 50px;
     34     line-height: 106px;
     35     text-align: center;
     36     font-weight: bold;
     37 }
     38 #inner .num2{
     39     color: #7c736a;
     40     background: #eee4da;
     41 }
     42 #inner .num4{
     43     color: #7c736a;
     44     background: #ece0c8;
     45 }
     46 #inner .num8{
     47     color: #fff7eb;
     48     background: #f2b179;
     49 }
     50 #inner .num16{
     51     color: #fff7eb;
     52     background: #f59563;
     53 }
     54 #inner .num32{
     55     color: #FFF7EB;
     56     background: #f57c5f;
     57 }
     58 #inner .num64{
     59     color: #FFF7EB;
     60     background: #f65d3b;
     61 }
     62 #inner .num128{
     63     color: #FFF7EB;
     64     background: #edce71;
     65 }
     66 #inner .num256{
     67     color: #FFF7EB;
     68     background: #edcc61;
     69 }
     70 #inner .num512{
     71     color: #FFF7EB;
     72     background: #ecc850;
     73 }
     74 #inner .num1024{
     75     font-size: 46px;
     76     color: #FFF7EB;
     77     background: #edc53f;
     78 }
     79 #inner .num2048{
     80     font-size: 46px;
     81     color: #FFF7EB;
     82     background: #eec22e;
     83 }
     84 #inner .num4096{
     85     font-size: 46px;
     86     color:#FFF7EB ;
     87     background: #3d3a33;
     88 }
     89 
     90 /*num2动画*/
     91 .animate{
     92     -webkit-animation: pulse 0.3s both;
     93      animation: pulse 0.3s both;
     94 }
     95 @-webkit-keyframes pulse {
     96   0% {
     97     -webkit-transform: scale3d(1, 1, 1);
     98     transform: scale3d(1, 1, 1);
     99   }
    100 
    101   50% {
    102     -webkit-transform: scale3d(1.1, 1.1, 1.1);
    103     transform: scale3d(1.1, 1.1, 1.1);
    104   }
    105 
    106   100% {
    107     -webkit-transform: scale3d(1, 1, 1);
    108     transform: scale3d(1, 1, 1);
    109   }
    110 }
    111 
    112 @keyframes pulse {
    113   0% {
    114     -webkit-transform: scale3d(1, 1, 1);
    115     -ms-transform: scale3d(1, 1, 1);
    116     transform: scale3d(1, 1, 1);
    117   }
    118 
    119   50% {
    120     -webkit-transform: scale3d(1.1, 1.1, 1.1);
    121     -ms-transform: scale3d(1.1, 1.1, 1.1);
    122     transform: scale3d(1.1, 1.1, 1.1);
    123   }
    124 
    125   100% {
    126     -webkit-transform: scale3d(1, 1, 1);
    127     -ms-transform: scale3d(1, 1, 1);
    128     transform: scale3d(1, 1, 1);
    129   }
    130 }
    PC端
      1 @charset "utf-8";
      2 
      3 body,h1,h2,h3,h4,p,dl,dd,ul,ol,form,input,textarea,th,td,select{margin: 0;padding: 0;}
      4 em{font-style: normal;}
      5 li{list-style: none;}
      6 a{text-decoration: none;}
      7 img{border: none;vertical-align: top;margin: 0;padding: 0;}
      8 table{border-collapse: collapse;}
      9 input,textarea{outline: none;}
     10 textarea{resize:none;overflow: auto;}
     11 body{font-size:12px;font-family: arial;}
     12 
     13 body,html{
     14     width: 100%;
     15     height: 100%;
     16 }
     17 #wrap{
     18     position: relative;
     19     width: 94%;
     20     left: 3%;
     21     top: 5%;
     22     background: #b8af9e;
     23     border-radius: 20px;
     24 }
     25 #bgPic{
     26     width: 100%;
     27     border-radius: 20px;
     28 }
     29 #bgPic img{
     30     width: 100%;
     31 }
     32 #inner{
     33     width: 93.6%;
     34     height: 93.6%;
     35     left: 3.2%;
     36     top: 3.2%;
     37     position: absolute;
     38 }
     39 
     40 #inner div{
     41     position: absolute;
     42     width: 22.435897%;
     43     height: 22.435897%;
     44     background: #eee4da;
     45     font-size: 4em;
     46     line-height: 1.2;
     47     text-align: center;
     48     font-weight: bold;
     49 }
     50 #inner div:nth-child(1){
     51     left: 0;
     52     top: 0;
     53 }
     54 #inner div:nth-child(2){
     55     left: 25.854701%;
     56     top:  0;
     57 }
     58 #inner div:nth-child(3){
     59     left: 51.709402%;
     60     top: 0;
     61 }
     62 #inner div:nth-child(4){
     63     right: 0;
     64     top: 0;
     65 }
     66 #inner div:nth-child(5){
     67     left: 0;
     68     top: 25.854701%;
     69 }
     70 #inner div:nth-child(6){
     71     left: 25.854701%;
     72     top: 25.854701%;
     73 }
     74 #inner div:nth-child(7){
     75     left: 51.709402%;
     76     top: 25.854701%;
     77 }
     78 #inner div:nth-child(8){
     79     right: 0;
     80     top: 25.854701%;
     81 }
     82 #inner div:nth-child(9){
     83     left: 0;
     84     top: 51.709402%;
     85 }
     86 #inner div:nth-child(10){
     87     left: 25.854701%;
     88     top: 51.709402%;
     89 }
     90 #inner div:nth-child(11){
     91     left: 51.709402%;
     92     top: 51.709402%;
     93 }
     94 #inner div:nth-child(12){
     95     right: 0;
     96     top: 51.709402%;
     97 }
     98 #inner div:nth-child(13){
     99     left: 0;
    100     bottom: 0;
    101 }
    102 #inner div:nth-child(14){
    103     left: 25.854701%;
    104     bottom: 0;
    105 }
    106 #inner div:nth-child(15){
    107     left: 51.709402%;
    108     bottom: 0;
    109 }
    110 #inner div:nth-child(16){
    111     right: 0;
    112     bottom: 0;
    113 }
    114 
    115 
    116 #inner .num2{
    117     color: #7c736a;
    118     background: #eee4da;
    119 }
    120 #inner .num4{
    121     color: #7c736a;
    122     background: #ece0c8;
    123 }
    124 #inner .num8{
    125     color: #fff7eb;
    126     background: #f2b179;
    127 }
    128 #inner .num16{
    129     color: #fff7eb;
    130     background: #f59563;
    131 }
    132 #inner .num32{
    133     color: #FFF7EB;
    134     background: #f57c5f;
    135 }
    136 #inner .num64{
    137     color: #FFF7EB;
    138     background: #f65d3b;
    139 }
    140 #inner .num128{
    141     color: #FFF7EB;
    142     background: #edce71;
    143     font-size: 2.8em;
    144 }
    145 #inner .num256{
    146     color: #FFF7EB;
    147     background: #edcc61;
    148     font-size: 2.8em;
    149 }
    150 #inner .num512{
    151     color: #FFF7EB;
    152     background: #ecc850;
    153     font-size: 2.8em;
    154 }
    155 #inner .num1024{
    156     font-size: 2em;
    157     color: #FFF7EB;
    158     background: #edc53f;
    159 }
    160 #inner .num2048{
    161     font-size: 2em;
    162     color: #FFF7EB;
    163     background: #eec22e;
    164 }
    165 #inner .num4096{
    166     font-size: 2em;
    167     color:#FFF7EB ;
    168     background: #3d3a33;
    169 }
    170 #text{
    171     position: relative;
    172     width: 100%;
    173     height: 30px;
    174     font-size: 18px;
    175     color: #7C736A;
    176     text-align: center;
    177     line-height: 30px;
    178     margin: 0 auto;
    179     top: 8%;
    180 }
    181 /*num2动画*/
    182 .animate{
    183     -webkit-animation: pulse 0.2s both;
    184      animation: pulse 0.2s both;
    185 }
    186 @-webkit-keyframes pulse {
    187   0% {
    188     -webkit-transform: scale3d(1, 1, 1);
    189     transform: scale3d(1, 1, 1);
    190   }
    191 
    192   50% {
    193     -webkit-transform: scale3d(1.1, 1.1, 1.1);
    194     transform: scale3d(1.1, 1.1, 1.1);
    195   }
    196 
    197   100% {
    198     -webkit-transform: scale3d(1, 1, 1);
    199     transform: scale3d(1, 1, 1);
    200   }
    201 }
    202 
    203 @keyframes pulse {
    204   0% {
    205     -webkit-transform: scale3d(1, 1, 1);
    206     -ms-transform: scale3d(1, 1, 1);
    207     transform: scale3d(1, 1, 1);
    208   }
    209 
    210   50% {
    211     -webkit-transform: scale3d(1.1, 1.1, 1.1);
    212     -ms-transform: scale3d(1.1, 1.1, 1.1);
    213     transform: scale3d(1.1, 1.1, 1.1);
    214   }
    215 
    216   100% {
    217     -webkit-transform: scale3d(1, 1, 1);
    218     -ms-transform: scale3d(1, 1, 1);
    219     transform: scale3d(1, 1, 1);
    220   }
    221 }
    移动端

      2、功能模块

        PC端&移动端:

     1   var inner = document.getElementById("inner");
     2     var divs = inner.children;
     3     var Len = divs.length;//块数量
     4     //初始化标记
     5     for(var i=0;i< Len;i++){
     6         divs[i].index = 0;
     7     }
     8     //随机数2
     9     function addRan(){
    10         var ran = Math.floor(Math.random()*Len);
    11         var zero = 0;//判断空位
    12         for(var i=0;i<Len;i++){
    13                 if(divs[i].index==0){
    14                     zero++;
    15                 }            
    16         }
    17         //占满
    18         if(zero==0){
    19             return;
    20         }
    21         //空位
    22         if(divs[ran].index==0){
    23             divs[ran].innerHTML = 2;
    24             divs[ran].className = "num2 animate";
    25             divs[ran].index = 2;
    26             setTimeout(function(){
    27                 divs[ran].className = "num2";
    28             },100);
    29         }else{
    30             addRan();            
    31         }
    32         return;
    33     }
    34     //初始随机两个
    35     addRan();
    36     addRan();

      解释:首先给16数字DIV赋一个初值的属性值,即div[i].index = 0;创建一个随机函数,先判断空位数,数量大于0即生成;接着判断空位的标记值,如果不等于0则递归执行,重新生成一个随机数,直到符合条件。

     1     //相加
     2     function add(k,p){
     3         if(divs[k].index==divs[k+p].index && divs[k+p].index !=0){
     4             divs[k].innerHTML *=2;
     5             divs[k].index *=2; 
     6             divs[k].className = "num" + divs[k].index;
     7             divs[k+p].innerHTML = "";
     8             divs[k+p].className = "";
     9             divs[k+p].index = 0;
    10         }                
    11     }
    12     //移动
    13     function move(k,p){
    14         if(divs[k].index==0 && divs[k+p].index !=0){
    15             divs[k].innerHTML = divs[k+p].innerHTML;
    16             divs[k].className = divs[k+p].className;
    17             divs[k+p].innerHTML = "";
    18             divs[k+p].className = "";                
    19             divs[k].index = divs[k+p].index;
    20             divs[k+p].index = 0;
    21         }    
    22     }    

       解释:

        k代表的是存放相加结果或者移动到达后的div下标,而k+p则是相邻的div的下标。根据方向不一样,p可取±1(左右)和±4(上下)。

        相加条件成立时,即相邻两个div的值相等且其中一个不为零时,则把它们相加。然后K位的值自身乘2,标记值同步乘2,class改为相应的名;而被相加的DIV则清空内容,class名置空,index值置0。

        移动条件成立时,即相邻两个div一个标记为0而另一个不为0时,则向k位移动。移动时先将邻位的值赋给它,对应的class名也赋给它,然后将自身class和值都置空。最后给修改它们的标记值,K位等于邻位的标记,邻位标记置0。

     1     //左移
     2     function moveLeft(){
     3         function left(){
     4             for(var i=0;i<13;i+=4){
     5                 //重复三次
     6                 for(var t=0;t<3;t++){
     7                     for(var j=0;j<3;j++){
     8                             move(i+j,1);
     9                     }
    10                 }                
    11             }
    12         }
    13         left();
    14         //检测相等
    15                 for(var i=0;i<13;i+=4){
    16                     for(var j=0;j<3;j++){
    17                             add(i+j,1);
    18                     }
    19                 }            
    20         //重排
    21         left();
    22     }     
     1     //上移
     2     function moveUp(){
     3         function up(){
     4             for(var i=0;i<4;i++){
     5                 for(var t=0;t<3;t++){
     6                     for(var j =0; j<12; j+=4){
     7                             move(i+j,4);
     8                     }
     9                 }            
    10             }
    11         }    
    12         up();
    13         for(var i=0;i<4;i++){
    14             for(var t=0;t<12;t+=4){
    15                         add(i+t,4)
    16             }
    17         }
    18         //加完重排
    19         up();
    20     }
    21     //右移
    22     function moveRight(){
    23         function right(){
    24             for(var i=0;i<13;i+=4){        
    25                 for(var t=0;t<3;t++){//重复三次检测
    26                     for(var j=3;j>0;j--){
    27                             move(i+j,-1);
    28                     }
    29                 }            
    30             }
    31         }
    32         right();
    33         //检测相加
    34         for(var i=0;i<13;i+=4){
    35             for(var t=3;t>0;t--){
    36                     add(i+t,-1);
    37             }
    38         }            
    39         right();
    40     }
    41     //下移
    42     function moveDown(){
    43         function down(){
    44             for(var i=0;i<4;i++){    
    45                 for(var t=0;t<3;t++){//重复三次检测
    46                     for(var j=12;j>0;j-=4){
    47                             move(i+j,-4);
    48                     }
    49                 }            
    50             }
    51         }
    52         down();
    53         //相加
    54         for(var i=0;i<4;i++){
    55             for(var t=12;t>0;t-=4){
    56                     add(i+t,-4);
    57             }
    58         }        
    59         down();
    60     }    
    上下右移的代码

        根据移动的是水平还是垂直方向,判断先行后列还是先列后行的顺序,循环执行move函数。移动完后检查相邻的数字是否相等,遍历所有div并将符合条件的相加。由于位置改变了,所以最后还要移动重排一次,等待下一步操作。重复三次的for循环是为了检测同一行或同一列内,是否所有的空位都移动完了。上面几个函数代码还可以进一步复用,为了更明了我暂时就没融合它们了,过两天几天更新我在试试。

       PC端:

     1 //方向键
     2     document.onkeydown = function (e){
     3         var e = e || window.event;
     4         switch(e.keyCode){
     5             //
     6             case 37:    
     7                 moveLeft();
     8                 addRan();    //产生随机的2
     9                 return false;//取消方向键的默认事件,下同
    10                 break;
    11             //
    12             case 38:
    13                 moveUp();
    14                 addRan();
    15                 return false;
    16                 break;
    17             //
    18             case 39:
    19                 moveRight();            
    20                 addRan();
    21                 return false;
    22                 break;
    23             //
    24             case 40:
    25                 moveDown();
    26                 addRan();
    27                 return false;
    28                 break;
    29         }        
    30     }

      移动端:

     1     //滑动触摸屏幕
     2     var target = document.body;
     3     //取消默认事件
     4     touch.on(target,"touchstart",function(ev){
     5         ev.preventDefault();
     6     })
     7     //
     8     touch.on(target,"swipeleft",function(ev){
     9                 moveLeft();
    10                 addRan();    
    11     });
    12     //
    13     touch.on(target,"swipeup",function(ev){
    14                 moveUp();
    15                 addRan();    
    16     });
    17     //
    18     touch.on(target,"swiperight",function(ev){
    19                 moveRight();
    20                 addRan();    
    21     });
    22     //
    23     touch.on(target,"swipedown",function(ev){
    24                 moveDown();
    25                 addRan();    
    26     });

      解释:PC版的也可以用事件监听方法,不过都要记得取消方向键的默认事件,不然可能会移动滚动条。注意,移动版的我使用了百度的touch.js手势库http://touch.code.baidu.com/),所以记得引入touch.js文件。整个小游戏就大概这几个函数了,最后把它们一起放置在window.onload里面执行就好了。

      游戏地址:

      PC端:www.chengguanhui.com/demos/2048

      手机端:www.chengguanhui.com/demos/2048_mobile

      说明:原创文章,有错之处,望多指教。本文仅供学习与交流,转载时请注明出处。谢谢。

  • 相关阅读:
    LINQ N~layer!
    CodeSmith 破解和注册
    鼠标划过链接时弹出窗口
    ajax的loading显示在gridview的位置
    Asp.net与SQL一起打包部署安装
    编译apache报错 No recognized SSL/TLS toolkit detected
    ubuntu12.04 卸载和安装mysql
    Ubuntu12.04 英文环境下使用ibus输入中文并自动启动输入法
    Ubuntu11.10 源码编译安装PHP5.3.8 [转]
    debian 下 编译安装 mysql 二进制文件
  • 原文地址:https://www.cnblogs.com/chengguanhui/p/4693518.html
Copyright © 2020-2023  润新知