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                     width:'0px',
12                     height:'0px',
13                     top:getPos(i) + 50,
14                     left:getPos(j) + 50
15                 })
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 }
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                  width:'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聯系我

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM