最近用Jqery寫了一個1024小游戲,由於是第一次寫小游戲,所以就選了一個基礎的沒什么難度游戲。具體實現如下:
首先在開發時將整個游戲分成兩層(自認為),底層是游戲的數據結構以及對數據的操作,上層是顯示出來的用戶界面。底層選擇使用一個4x4的二維數組,整個游戲的數據操作都圍繞着這個二維數組進行。
【一】游戲基礎界面
1 <div id="game"> 2 <div id="header"> 3 <h1>1024</h1> 4 <button id="newGame">開始新的游戲</button> 5 <p>分數:<span id="score">0</span> 最高分:<span id="maxScore">0</span></p> 6 <div id="movescore"><p>+16</p></div> 7 </div> 8 <div id="container"> 9 <div class="cell" id="cell-0-0"></div> 10 <div class="cell" id="cell-0-1"></div> 11 <div class="cell" id="cell-0-2"></div> 12 <div class="cell" id="cell-0-3"></div> 13 <div class="cell" id="cell-1-0"></div> 14 <div class="cell" id="cell-1-1"></div> 15 <div class="cell" id="cell-1-2"></div> 16 <div class="cell" id="cell-1-3"></div> 17 <div class="cell" id="cell-2-0"></div> 18 <div class="cell" id="cell-2-1"></div> 19 <div class="cell" id="cell-2-2"></div> 20 <div class="cell" id="cell-2-3"></div> 21 <div class="cell" id="cell-3-0"></div> 22 <div class="cell" id="cell-3-1"></div> 23 <div class="cell" id="cell-3-2"></div> 24 <div class="cell" id="cell-3-3"></div> 25 </div> 26 <div class="gameover"> 27 <div id="gameoverText"> 28 <p></p> 29 </div> 30 <p id="gameoverScore"></p> 31 <div id="reStart"> 32 <button id="reStartBtn">再玩一次</button> 33 </div> 34 </div> 35 </div>
CSS:
1 *{ 2 margin: 0; 3 padding: 0; 4 } 5 #game{ 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;/*設置 h1、h2、h3、h4 元素的文本修飾*/ 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 font-size: 3.5em; 47 font-weight:700; 48 text-align: center; 49 line-height:100px; 50 } 51 #newGame{ 52 width: 120px; 53 height: 30px; 54 border-radius: 5px; 55 border: 1px solid rgb(143,122,102); 56 background-color: rgb(143,122,102); 57 color: white; 58 margin-top: 10px; 59 margin-bottom: 10px; 60 } 61 .gameover{ 62 width: 100%; 63 height: 500px; 64 background-color: rgba(255,255,255,0.5); 65 position: absolute; 66 top:153px; 67 display: none; 68 } 69 #gameoverText{ 70 font-size: 4em; 71 font-weight: 600; 72 color: #363636; 73 text-align: center; 74 opacity: 1; 75 padding-top: 10%; 76 } 77 #reStartBtn{ 78 width: 100px; 79 height: 40px; 80 border-radius: 5px; 81 border: 1px solid rgb(143,122,102); 82 background-color: rgb(143,122,102); 83 color: white; 84 margin-top: 3%; 85 } 86 #gameoverScore{ 87 font-size: 1.5em; 88 } 89 #movescore{ 90 position: absolute; 91 text-align: center; 92 padding-left: 45%; 93 top:110px; 94 font-weight: 600; 95 display: none; 96 }
此時界面的16個格子是重疊在一起的,給每個格子寫css比較麻煩,所以通過js循環進行設置:
1 //初始化繪制表格 2 function printTab(){ 3 for(var i=0;i<4;i++){ 4 for(var j=0;j<4;j++){ 5 var cell=$('#cell-'+i+'-'+j); 6 cell.css({top:(20+i*120),left:(20+j*120)}); 7 } 8 } 9 }
不同的數字顯示不同的背景顏色與文字顏色:
1 function getBackgroundColor(number){ 2 switch (number) { 3 case 2:return "#eee4da";break; 4 case 4:return "#ede0c8";break; 5 case 8:return "#f2b179";break; 6 case 16:return "#f59563";break; 7 case 32:return "#f67c5f";break; 8 case 64:return "#f65e3b";break; 9 case 128:return "#edcf72";break; 10 case 256:return "#edcc61";break; 11 case 512:return "#9c0";break; 12 case 1024:return "#33b5e5";break; 13 case 2048:return "#09c";break; 14 case 4096:return "#a6c";break; 15 case 8192:return "#93c";break; 16 } 17 } 18 // 設置相應數字的文字顏色 19 function getColor(number){ 20 if (number <= 4) { 21 return "#776e65" 22 } 23 return "white"; 24 }
每次操作后根據當前二維數組進行重繪畫面:
1 //根據二維數組繪制畫面 2 function rePrint(checkerboard){ 3 for(var i=0;i<4;i++){ 4 for(var j=0;j<4;j++){ 5 if(checkerboard[i][j]!=0){ 6 var printCell=$('#cell-'+i+'-'+j); 7 printCell.css('background-color',getBackgroundColor(checkerboard[i][j])); 8 printCell.css('color',getColor(checkerboard[i][j])); 9 printCell.text(checkerboard[i][j]); 10 if(checkerboard[i][j]>=1024){ 11 printCell.css('font-size','2.5em'); 12 printCell.css('font-weight','500'); 13 } 14 }else{ 15 var printCell=$('#cell-'+i+'-'+j); 16 printCell.css('background-color','#ccc0b3'); 17 printCell.css('color','black'); 18 printCell.text(''); 19 } 20 } 21 } 22 }
【二】游戲邏輯
除了需要定義一個二維數組checkerboard,還需要定義一個存總分的變量score,一個存每次操作得分的變量addScore,一個記錄鍵盤是否可以操作的變量ableKeyDown,0可以響應鍵盤操作,1禁止鍵盤操作。
①游戲初始化
初始化封裝成一個函數方便后續的【開始新的游戲】以及【再玩一次】功能。
1 //初始化游戲 2 function newgame(){ 3 ableKeyDown=0; 4 score=0; 5 checkerboard=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]; 6 $('#score').text('0'); 7 $('#maxScore').text(window.localStorage.getItem("maxScore")); 8 rePrint(checkerboard); 9 randNum(checkerboard); 10 randNum(checkerboard); 11 }
②在隨即位置生成2或4
根據游戲規則,在游戲剛開始時會在兩個隨即位置分別生成2或4,在每次操作后若游戲沒結束,會在一個空的位置隨機生成一個2或4,將這一功能封裝成一個函數,Math.random()生成的隨機數范圍是[0,1),由於二維數組范圍是[0,3],所以通過Math.floor(Math.random()*4)將得到0-3的隨機數。另外需要隨機得到2或4,定義一個數組initRandNum=[2,4],只需隨機得到initRandNum[0]或initRandNum[1]即可,所以同理使用Math.floor(Math.random()*2)隨機得到1或2即可。
1 function randNum(checkerboard){//在隨機位置隨機產生2或4 2 var randX = Math.floor(Math.random()*4); 3 var randY = Math.floor(Math.random()*4); 4 var initRandNum=[2,4]; 5 var randNum=Math.floor(Math.random()*2); 6 var randVal=initRandNum[randNum]; 7 while(true){ 8 if(checkerboard[randX][randY]==0){ 9 break; 10 }else{ 11 var randX = Math.floor(Math.random()*4); 12 var randY = Math.floor(Math.random()*4); 13 } 14 } 15 checkerboard[randX][randY]=randVal; 16 printRandNum(randX,randY,randVal);//將randNum()繪制出來 17 }
在得到上述隨即位置的2或4后需要將其繪制出來:
1 function printRandNum(randX,randY,randVal){ 2 var printRandCell=$('#cell-'+randX+'-'+randY); 3 printRandCell.css('background-color',getBackgroundColor(randVal)); 4 printRandCell.css('color',getColor(randVal)); 5 printRandCell.text(randVal); 6 }
③游戲中最重要的兩個函數
即判斷當前能否移動的函數以及如何移動合並的函數。
1)--------判斷能否移動,以左移為例
根據對游戲的觀察發現在兩中情況下可以移動,一是存在某個不為零的方塊,該數字左邊為空,即其左邊的數組值為0;二是存在某個不為零的方塊,,該數字左邊與其相同,即其左邊的數組值與其相等。對二維數組進行循環,若滿足上述條件就返回1,即可以移動。
1 function canMoveLeft(checkerboard){ 2 for(var i=0;i<4;i++){ 3 for(var j=0;j<4;j++){ 4 if(checkerboard[i][j]!=0){ 5 if(j!=0){ 6 if(checkerboard[i][j-1]==0||checkerboard[i][j-1]==checkerboard[i][j]){ 7 return 1; 8 break; 9 } 10 } 11 } 12 } 13 } 14 }
2)--------移動合並函數,以左移為例
這個應該是游戲中最重要的函數,根據游戲規則,1)若一個非零方塊左邊全部為空,它將移動到最左邊,若左邊的某個不為0,它將移動到這個不為0方塊的右邊;2)若一個非零方塊與左邊的數字相同,它將移動到左邊並與之合並相加;3)若一個非零方塊與左邊的數字相同,他與左邊合並后的值恰巧與再左邊的數相同,此時應該只進行第一次合並,不進行第二次合並。例如當前一排是4,2,2,0,其移動后結果應該是4,4,0,0,而不是8,0,0,0。
第三點尤為重要,最開始由於忽略了這點寫出來后結果是不對的。我真對3)的解決辦法是,在每次循環一行時初始化一個長度為4的數組var tag=[0,0,0,0],若某個數在此次循環制相加過一次,就在tag相應位置置為1,再然后在每次合並前做一次判斷,若該tag為1,則不進行相加。
1 function moveLeft(checkerboard){ 2 addscore=0; 3 for(var i=0;i<4;i++){ 4 var tag=[0,0,0,0]; 5 for(var j=1;j<4;j++){ 7 if(checkerboard[i][j]!=0){ 8 if(checkerboard[i][j-1]==0){//左邊為空時 9 for(k=j-1;k>=0;k--){ 10 if(checkerboard[i][k]!=0){ 11 checkerboard[i][k+1]=checkerboard[i][j]; 12 checkerboard[i][j]=0; 13 if(checkerboard[i][k+1]==checkerboard[i][k]){//移動后與左邊相等 14 if(tag[k]==0&&tag[k+1]==0){ 15 checkerboard[i][k]+=checkerboard[i][k+1]; 16 score += checkerboard[i][k]; 17 addscore+=checkerboard[i][k]; 18 tag[k]=1;20 checkerboard[i][k+1]=0; 21 } 22 } 23 break; 24 }else if(k==0){//左邊全空 25 checkerboard[i][0]=checkerboard[i][j]; 26 checkerboard[i][j]=0; 27 break; 28 } 29 } 30 }else if(checkerboard[i][j-1]==checkerboard[i][j]){//左邊相等時 31 if(tag[j-1]==0&&tag[j]==0){ 32 checkerboard[i][j-1]+=checkerboard[i][j]; 33 score += checkerboard[i][j-1]; 34 addscore+=checkerboard[i][j-1]; 35 tag[j-i]=1;37 checkerboard[i][j]=0; 38 } 39 } 40 } 41 } 42 } 43 rePrint(checkerboard);//停止移動后重繪畫面 44 }
值得注意的是,為了計算游戲的總分以及每次操作的得分,需要在每次合並后對addScore和score進行計算。
右移、上移、下移與左移邏輯基本相同,只需對左移代碼稍作修改即可。
④關於分數
1 //更新分數 2 function updateScore(num){ 3 $('#score').text(num); 4 }
在每次得分后會在總分位置產生一個加分動畫
1 //分數動畫 2 function movescore(score){ 3 if(score>0){ 4 $('#movescore p').text('+'+score); 5 $('#movescore').css('top','110px'); 6 $('#movescore').show(); 7 var top; 8 var topVal=110; 9 var opacityVal=1; 10 var timer=null; 11 timer=setInterval(function(){ 12 topVal-=5; 13 opacityVal-=0.1; 14 top=topVal+'px'; 15 $('#movescore').css('top',top); 16 $('#movescore').css('opacity',opacityVal); 17 if(topVal<50){ 18 clearInterval(timer); 19 $('#movescore').hide(); 20 } 21 },40); 22 } 23 }
⑤游戲結束
若二維數組中某一項等於1024游戲結束,顯示最終得分,並設置鍵盤不可繼續操作;或者當前無法繼續移動游戲結束,顯示最終得分。
1 //游戲結束函數 2 function gameOver(checkerboard){ 3 if(canMoveLeft(checkerboard)!=1&&canMoveRight(checkerboard)!=1&&canMoveUp(checkerboard)!=1&&canMoveDown(checkerboard)!=1){ 4 $('.gameover').show(); 5 $('#gameoverText p').text('Game Over !'); 6 $('#gameoverScore').text('最終得分'+score); 7 if(score>window.localStorage.getItem("maxScore")){ 8 window.localStorage.setItem("maxScore",score); 9 $('#maxScore').text(window.localStorage.getItem("maxScore")); 10 } 11 } 12 for(var i=0;i<4;i++){ 13 for(var j=0;j<4;j++){ 14 if(checkerboard[i][j]==1024){ 15 ableKeyDown=1;//鍵盤不可操作 16 $('.gameover').show(); 17 $('#gameoverText p').text('Congratulations!'); 18 $('#gameoverScore').text('最終得分'+score); 19 if(score>window.localStorage.getItem("maxScore")){ 20 window.localStorage.setItem("maxScore",score); 21 $('#maxScore').text(window.localStorage.getItem("maxScore")); 22 } 23 break; 24 } 25 } 26 } 27 }
⑥開始新游戲&再玩一次
1 //開始新游戲 2 $('#newGame').click(function(){ 3 newgame(); 4 }); 5 //再玩一次 6 $('#reStartBtn').click(function(){ 7 $('.gameover').hide(); 8 newgame(); 9 });