最近用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 });