View Post
2048小游戏实现思路
2048是一个比较的js与html结合的小游戏案例:
首先编写代码之前我们需要考虑代码的实现步骤:
- 实现客户端静态页面
- 随机数字的生成(主要是2和4,且每次操作之后都会生产一个随机数知道游戏结束)
- 实现上下左右操作
下面是一个我写的实例:
首先我们需要将客户端静态页面搭建好
代码如下:
这里的#div#盒子显示的是2048每个块的原始背景
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>2048</title> 5 <body> 6 <header> 7 <h1>2048</h1> 8 <a href="javascript:newgame();" id="newgamebutton">New Game</a> 9 <p>step : <span id="step">0</span></p> 10 </header> 11 <div id="grid-container"> 12 <div class="grid-cell" id="grid-cell-0-0"></div> 13 <div class="grid-cell" id="grid-cell-0-1"></div> 14 <div class="grid-cell" id="grid-cell-0-2"></div> 15 <div class="grid-cell" id="grid-cell-0-3"></div> 16 <div class="grid-cell" id="grid-cell-1-0"></div> 17 <div class="grid-cell" id="grid-cell-1-1"></div> 18 <div class="grid-cell" id="grid-cell-1-2"></div> 19 <div class="grid-cell" id="grid-cell-1-3"></div> 20 <div class="grid-cell" id="grid-cell-2-0"></div> 21 <div class="grid-cell" id="grid-cell-2-1"></div> 22 <div class="grid-cell" id="grid-cell-2-2"></div> 23 <div class="grid-cell" id="grid-cell-2-3"></div> 24 <div class="grid-cell" id="grid-cell-3-0"></div> 25 <div class="grid-cell" id="grid-cell-3-1"></div> 26 <div class="grid-cell" id="grid-cell-3-2"></div> 27 <div class="grid-cell" id="grid-cell-3-3"></div> 28 </div> 29 </body> 30 </html>
然后将页面稍微美化一下
css代码如下:
这里我将背景#div#运用绝对定位(方便用js代码将其排列),我把大盒子的大小设置为500px的正方形
每个小快设置为100px的正方形,互相间隔20px。
1 header{ 2 display: block; 3 margin: 0 auto; 4 width: 500px; 5 text-align: center; 6 } 7 header h1{ 8 font-family: Arial; 9 font-size: 60px; 10 font-weight: bold; 11 } 12 header #newgamebutton{ 13 display: block; 14 margin: 20px auto; 15 width: 100px; 16 padding:10px 10px; 17 background-color: #8f7a66; 18 font-family: Arial; 19 color: white; 20 border-radius: 10px; 21 text-decoration: none; 22 } 23 header #newgamebutton:hover{ 24 background-color: #9f8b77; 25 } 26 header p{ 27 font-family: Arial; 28 font-size: 25px; 29 margin: 20px auto; 30 } 31 #grid-container{ 32 width: 460px; 33 height: 460px; 34 padding:20px; 35 margin: 50px auto; 36 background-color: #bbada0; 37 border-radius: 10px; 38 position: relative; 39 } 40 .grid-cell{ 41 width: 100px; 42 height: 100px; 43 border-radius: 6px; 44 background-color: #ccc0b3; 45 position: absolute; 46 }
接下来我们需要将背景快分开排列整齐
代码如下:
1 function init(){ 2 for(var i=0;i<4;i++) 3 for(var j=0;j<4;j++){ 4 var gridCell = $("#grid-cell-"+i+"-"+j); 5 gridCell.css('top',getPosTop(i,j)); 6 gridCell.css('left',getPosLeft(i,j)); 7 }17 } 18 19 function getPosTop(i,j){ 20 return 20+120*i; 21 } 22 23 function getPosLeft(i,j){ 24 return 20+120*j; 25 }
上一部分代码我们只是把背景快的位置定位好,我们还需要一些#div#快用来存储我们的数字,这样更
加方便我们后续对数字块移动的操作,我们可以将下面代码加入到上面的init()函数中去
1 for(var i=0;i<4;i++){ 2 board[i] = new Array(); 3 hasconflicted[i] = new Array; 4 for(var j=0;j<4;j++) 5 board[i][j]=0; 6 hasconflicted[i][j]=false; 7 } 8 updateBoardView();
其中的board数组是事先声明好的,用来计算每一个快的位置,是一个4X4的二维数组,我们将他初始
化为0;方便后面的一系列操作,其中的updateBoardView()函数是一个生成数字快div的函数,另外
我们还需要将不同的数字设置不同的颜色,代码如下:
1 function updateBoardView(){ 2 $(".number-cell").remove(); 3 for(var i=0;i<4;i++) 4 for(var j=0;j<4;j++){ 5 $("#grid-container").append('<div class="number-cell" id="number-cell-'+i+'-'+j+'"></div>'); 6 var theNumberCell = $('#number-cell-'+i+'-'+j); 7 if(board[i][j] == 0){ 8 theNumberCell.css('width','0px'); 9 theNumberCell.css('height','0px'); 10 theNumberCell.css('top',getPosTop(i,j)+50); 11 theNumberCell.css('left',getPosLeft(i,j)+50); 12 } 13 else{ 14 theNumberCell.css('width','100px'); 15 theNumberCell.css('height','100px'); 16 theNumberCell.css('top',getPosTop(i,j)); 17 theNumberCell.css('left',getPosLeft(i,j)); 18 theNumberCell.css('background-color',getNumberBackgroundColor(board[i][j])); 19 theNumberCell.css('color',getNumberColor(board[i][j])); 20 theNumberCell.text(board[i][j]); 21 } 22 hasconflicted[i][j]=false; 23 } 24 } 25 26 function getNumberBackgroundColor(number){ 27 switch(number){ 28 case 2:return'#eee4de';break; 29 case 4:return'#ede0c8';break; 30 case 8:return'#f2b179';break; 31 case 16:return'#f59563';break; 32 case 32:return'#f67c5f';break; 33 case 64:return'#f65e3b';break; 34 case 128:return'#edcf72';break; 35 case 256:return'#edcc61';break; 36 case 512:return'#9c0';break; 37 case 1024:return'#33b5e5';break; 38 case 2048:return'#09c';break; 39 case 4096:return'#a6c';break; 40 case 8192:return'#93c';break; 41 } 42 return "black"; 43 } 44 45 function getNumberColor(number){ 46 if (number<=4) { 47 return "#776e65"; 48 } 49 return "white"; 50 }
上部分代码中的css:
1 .number-cell{ 2 border-radius: 6px; 3 font-family: Arial; 4 font-weight: bold; 5 font-size: 60px; 6 line-height: 100px; 7 text-align: center; 8 position: absolute; 9 }
然后我们需要编写一个随机生成2的倍数的函数,为了让生成数字时看起来更流畅,我还添加了一些动
画效果,代码如下:
1 function generateOneNumber(){ 2 if(nospace(board)) 3 return false; 4 var randx = parseInt(Math.floor(Math.random()*4)); 5 var randy = parseInt(Math.floor(Math.random()*4)); 6 var timer = 0; 7 while(timer<50){ 8 if(board[randx][randy]==0) 9 break; 10 randx = parseInt(Math.floor(Math.random()*4)); 11 randy = parseInt(Math.floor(Math.random()*4)); 12 timer++; 13 } 14 if(timer==50){ 15 for(var i=0;i<4;i++) 16 for(var j=0;j<4;j++){ 17 randx=i; 18 randy=j; 19 } 20 } 21 var randNumber = Math.random()<0.5?2:4; 22 board[randx][randy] =randNumber; 23 showNumberWithAnimation(randx,randy,randNumber); 24 return true; 25 } 26 27 function showNumberWithAnimation(i,j,randNumber) { 28 var numberCell = $('#number-cell-'+i+'-'+j); 29 numberCell.css('background-color',getNumberBackgroundColor(randNumber)); 30 numberCell.css('color',getNumberColor(randNumber)); 31 numberCell.text(randNumber); 32 numberCell.animate({ 33 width:"100px", 34 height:"100px", 35 top:getPosTop(i,j), 36 left:getPosLeft(i,j) 37 },50); 38 } 39 40 function nospace(board){ 41 for(var i=0;i<4;i++) 42 for(var j=0;j<4;j++) 43 if(board[i][j]==0) 44 return false; 45 return true; 46 }
接下来就是最重要的部分了,我们需要监听电脑键盘的敲击事件keydown,然后需要根据上下左右
每个键的keyCode值做出判断,这里我在例子中就只用左键为例:
1 $(document).keydown(function(event){ 2 switch(event.keyCode){ 3 case 37: //left 4 moveLeft(); 5 setTimeout("isgameover()",300); 6 setTimeout("generateOneNumber()",210); 7 break; 8 case 38: //up 9 moveUp(); 10 setTimeout("isgameover()",300); 11 setTimeout("generateOneNumber()",210); 12 break; 13 case 39: //right 14 moveRight(); 15 setTimeout("isgameover()",300); 16 setTimeout("generateOneNumber()",210); 17 break; 18 case 40: //down 19 moveDown() 20 setTimeout("isgameover()",300); 21 setTimeout("generateOneNumber()",210); 22 break; 23 default: 24 break; 25 } 26 }) 27 28 function moveLeft(){ 29 if(!canMoveLeft(board)) 30 return false; 31 for(var i=0;i<4;i++) 32 for(var j=1;j<4;j++){ 33 if(board[i][j]!=0){ 34 for(var k = 0 ; k < j ; k ++ ){ 35 if( board[i][k] == 0 && noBlockHorizontal( i , k , j , board ) ){ 36 showMoveAnimation( i , j , i ,k ); 37 board[i][k] = board[i][j]; 38 board[i][j] = 0; 39 continue; 40 } 41 else if(board[i][k]==board[i][j]&&noBlockHorizontal(i,k,j,board)&&!hasconflicted[i][k]){ 42 showMoveAnimation(i,j,i,k); 43 board[i][k]+=board[i][j]; 44 board[i][j]=0; 45 hasconflicted[i][k]=true; 46 continue; 47 } 48 } 49 } 50 } 51 setTimeout("updateBoardView()",200); 52 return true; 53 } 54 55 function canMoveLeft(){ 56 for(var i=0;i<4;i++) 57 for(var j=1;j<4;j++) 58 if(board[i][j]!=0) 59 if(board[i][j-1]==0||board[i][j-1]==board[i][j]) 60 return true; 61 return false; 62 } 63 64 function noBlockHorizontal(row,col1,col2,board){ 65 for(var i=col1+1;i<col2;i++) 66 if(board[row][i]!=0) 67 return false; 68 return true; 69 } 70 71 function showMoveAnimation(fromx,fromy,tox,toy){ 72 var numberCell = $('#number-cell-'+fromx+'-'+fromy); 73 numberCell.animate({ 74 top:getPosTop(tox,toy), 75 left:getPosLeft(tox,toy) 76 },200); 77 }
最后我们再为游戏添加一下结束提醒以及步骤提示来美化一下就行了:
1 function isgameover(){ 2 if(nospace(board)&&nomove(board)) 3 gameover(); 4 } 5 6 function gameover(){ 7 alert("GameOver"); 8 } 9 10 function nomove(){ 11 if(canMoveDown(board)||canMoveRight(board)||canMoveUp(board)||canMoveLeft(board)) 12 return false; 13 return true; 14 }
将下面代码插入keydown事件监听函数:
1 $("#step").text(step);
再将下面代码插入到每一个键盘的上下左右点击函数就行了:
1 step++;
总代码如下:
html部分:
<!DOCTYPE html> <html> <head> <title>2048</title> <link rel="stylesheet" type="text/css" href="2048.css"> <script type="text/javascript" src="http://libs.baidu.com/jquery/1.9.0/jquery.min.js"></script> <script type="text/javascript" src="support2048.js"></script> <script type="text/javascript" src="showanimation2048.js"></script> <script type="text/javascript" src="main2048.js"></script> </head> <body> <header> <h1>2048</h1> <a href="javascript:newgame();" id="newgamebutton">New Game</a> <p>step : <span id="step">0</span></p> </header> <div id="grid-container"> <div class="grid-cell" id="grid-cell-0-0"></div> <div class="grid-cell" id="grid-cell-0-1"></div> <div class="grid-cell" id="grid-cell-0-2"></div> <div class="grid-cell" id="grid-cell-0-3"></div> <div class="grid-cell" id="grid-cell-1-0"></div> <div class="grid-cell" id="grid-cell-1-1"></div> <div class="grid-cell" id="grid-cell-1-2"></div> <div class="grid-cell" id="grid-cell-1-3"></div> <div class="grid-cell" id="grid-cell-2-0"></div> <div class="grid-cell" id="grid-cell-2-1"></div> <div class="grid-cell" id="grid-cell-2-2"></div> <div class="grid-cell" id="grid-cell-2-3"></div> <div class="grid-cell" id="grid-cell-3-0"></div> <div class="grid-cell" id="grid-cell-3-1"></div> <div class="grid-cell" id="grid-cell-3-2"></div> <div class="grid-cell" id="grid-cell-3-3"></div> </div> </body> </html>
css部分:
header{ display: block; margin: 0 auto; width: 500px; text-align: center; } header h1{ font-family: Arial; font-size: 60px; font-weight: bold; } header #newgamebutton{ display: block; margin: 20px auto; width: 100px; padding:10px 10px; background-color: #8f7a66; font-family: Arial; color: white; border-radius: 10px; text-decoration: none; } header #newgamebutton:hover{ background-color: #9f8b77; } header p{ font-family: Arial; font-size: 25px; margin: 20px auto; } #grid-container{ width: 460px; height: 460px; padding:20px; margin: 50px auto; background-color: #bbada0; border-radius: 10px; position: relative; } .grid-cell{ width: 100px; height: 100px; border-radius: 6px; background-color: #ccc0b3; position: absolute; } .number-cell{ border-radius: 6px; font-family: Arial; font-weight: bold; font-size: 60px; line-height: 100px; text-align: center; position: absolute; }
js部分:
var board = new Array(); var step = 0; var hasconflicted = new Array(); $(document).ready(function(){ newgame(); }); function newgame(){ init(); generateOneNumber(); generateOneNumber(); } function init(){ for(var i=0;i<4;i++) for(var j=0;j<4;j++){ var gridCell = $("#grid-cell-"+i+"-"+j); gridCell.css('top',getPosTop(i,j)); gridCell.css('left',getPosLeft(i,j)); } for(var i=0;i<4;i++){ board[i] = new Array(); hasconflicted[i] = new Array; for(var j=0;j<4;j++) board[i][j]=0; hasconflicted[i][j]=false; } updateBoardView(); } function updateBoardView(){ $(".number-cell").remove(); for(var i=0;i<4;i++) for(var j=0;j<4;j++){ $("#grid-container").append('<div class="number-cell" id="number-cell-'+i+'-'+j+'"></div>'); var theNumberCell = $('#number-cell-'+i+'-'+j); if(board[i][j] == 0){ theNumberCell.css('width','0px'); theNumberCell.css('height','0px'); theNumberCell.css('top',getPosTop(i,j)+50); theNumberCell.css('left',getPosLeft(i,j)+50); } else{ theNumberCell.css('width','100px'); theNumberCell.css('height','100px'); theNumberCell.css('top',getPosTop(i,j)); theNumberCell.css('left',getPosLeft(i,j)); theNumberCell.css('background-color',getNumberBackgroundColor(board[i][j])); theNumberCell.css('color',getNumberColor(board[i][j])); theNumberCell.text(board[i][j]); } hasconflicted[i][j]=false; } } function generateOneNumber(){ if(nospace(board)) return false; var randx = parseInt(Math.floor(Math.random()*4)); var randy = parseInt(Math.floor(Math.random()*4)); var timer = 0; while(timer<50){ if(board[randx][randy]==0) break; randx = parseInt(Math.floor(Math.random()*4)); randy = parseInt(Math.floor(Math.random()*4)); timer++; } if(timer==50){ for(var i=0;i<4;i++) for(var j=0;j<4;j++){ randx=i; randy=j; } } var randNumber = Math.random()<0.5?2:4; board[randx][randy] =randNumber; showNumberWithAnimation(randx,randy,randNumber); return true; } $(document).keydown(function(event){ switch(event.keyCode){ case 37: //left moveLeft(); setTimeout("isgameover()",300); setTimeout("generateOneNumber()",210); break; case 38: //up moveUp(); setTimeout("isgameover()",300); setTimeout("generateOneNumber()",210); break; case 39: //right moveRight(); setTimeout("isgameover()",300); setTimeout("generateOneNumber()",210); break; case 40: //down moveDown() setTimeout("isgameover()",300); setTimeout("generateOneNumber()",210); break; default: break; } $("#step").text(step); }) function isgameover(){ if(nospace(board)&&nomove(board)) gameover(); } function gameover(){ alert("GameOver"); } function moveLeft(){ if(!canMoveLeft(board)) return false; for(var i=0;i<4;i++) for(var j=1;j<4;j++){ if(board[i][j]!=0){ for(var k = 0 ; k < j ; k ++ ){ if( board[i][k] == 0 && noBlockHorizontal( i , k , j , board ) ){ showMoveAnimation( i , j , i ,k ); board[i][k] = board[i][j]; board[i][j] = 0; continue; } else if(board[i][k]==board[i][j]&&noBlockHorizontal(i,k,j,board)&&!hasconflicted[i][k]){ showMoveAnimation(i,j,i,k); board[i][k]+=board[i][j]; board[i][j]=0; hasconflicted[i][k]=true; continue; } } } } setTimeout("updateBoardView()",200); step++; return true; } function moveUp(){ if(!canMoveUp(board)) return false; for(var j=0;j<4;j++) for(var i=1;i<4;i++){ if(board[i][j]!=0){ for(var k = 0 ; k < i ; k ++ ){ if( board[k][j] == 0 && noBlockVertical( k, i , j , board ) ){ showMoveAnimation( i , j , k,j); board[k][j] = board[i][j]; board[i][j] = 0; continue; } else if(board[k][j]==board[i][j]&&noBlockVertical(k,i,j,board)&&!hasconflicted[k][j]){ showMoveAnimation(i,j,k,j); board[k][j]+=board[i][j]; board[i][j]=0; hasconflicted[k][j]=true; continue; } } } } setTimeout("updateBoardView()",200); step++; return true; } function moveRight(){ if(!canMoveRight(board)) return false; for(var i=0;i<4;i++) for(var j=2;j>=0;j--){ if(board[i][j]!=0){ for(var k = 3 ; k > j ; k -- ){ if( board[i][k] == 0 && noBlockHorizontal( i , j, k , board ) ){ showMoveAnimation( i , j , i ,k ); board[i][k] = board[i][j]; board[i][j] = 0; continue; } else if(board[i][k]==board[i][j]&&noBlockHorizontal(i,j,k,board)&&!hasconflicted[i][k]){ showMoveAnimation(i,j,i,k); board[i][k]+=board[i][j]; board[i][j]=0; hasconflicted[i][k]=true; continue; } } } } setTimeout("updateBoardView()",200); step++; return true; } function moveDown(){ if(!canMoveDown(board)) return false; for(var j=0;j<4;j++) for(var i=2;i>=0;i--){ if(board[i][j]!=0){ for(var k = 3 ; k > i ; k -- ){ if( board[k][j] == 0 && noBlockVertical( i, k , j , board ) ){ showMoveAnimation( i , j , k,j); board[k][j] = board[i][j]; board[i][j] = 0; continue; } else if(board[k][j]==board[i][j]&&noBlockVertical(i,k,j,board)&&!hasconflicted[k][j]){ showMoveAnimation(i,j,k,j); board[k][j]+=board[i][j]; board[i][j]=0; hasconflicted[k][j]=true; continue; } } } } setTimeout("updateBoardView()",200); step++; return true; } function showNumberWithAnimation(i,j,randNumber) { var numberCell = $('#number-cell-'+i+'-'+j); numberCell.css('background-color',getNumberBackgroundColor(randNumber)); numberCell.css('color',getNumberColor(randNumber)); numberCell.text(randNumber); numberCell.animate({ width:"100px", height:"100px", top:getPosTop(i,j), left:getPosLeft(i,j) },50); } function showMoveAnimation(fromx,fromy,tox,toy){ var numberCell = $('#number-cell-'+fromx+'-'+fromy); numberCell.animate({ top:getPosTop(tox,toy), left:getPosLeft(tox,toy) },200); } function getPosTop(i,j){ return 20+120*i; } function getPosLeft(i,j){ return 20+120*j; } function getNumberBackgroundColor(number){ switch(number){ case 2:return'#eee4de';break; case 4:return'#ede0c8';break; case 8:return'#f2b179';break; case 16:return'#f59563';break; case 32:return'#f67c5f';break; case 64:return'#f65e3b';break; case 128:return'#edcf72';break; case 256:return'#edcc61';break; case 512:return'#9c0';break; case 1024:return'#33b5e5';break; case 2048:return'#09c';break; case 4096:return'#a6c';break; case 8192:return'#93c';break; } return "black"; } function getNumberColor(number){ if (number<=4) { return "#776e65"; } return "white"; } function nospace(board){ for(var i=0;i<4;i++) for(var j=0;j<4;j++) if(board[i][j]==0) return false; return true; } function canMoveLeft(){ for(var i=0;i<4;i++) for(var j=1;j<4;j++) if(board[i][j]!=0) if(board[i][j-1]==0||board[i][j-1]==board[i][j]) return true; return false; } function canMoveUp(){ for(var j=0;j<4;j++) for(var i=1;i<4;i++) if(board[i][j]!=0) if(board[i-1][j]==0||board[i-1][j]==board[i][j]) return true; return false; } function canMoveRight(){ for(var i=0;i<4;i++) for(var j=2;j>=0;j--) if(board[i][j]!=0) if(board[i][j+1]==0||board[i][j+1]==board[i][j]) return true; return false; } function canMoveDown(){ for(var j=0;j<4;j++) for(var i=2;i>=0;i--) if(board[i][j]!=0) if(board[i+1][j]==0||board[i+1][j]==board[i][j]) return true; return false; } function noBlockHorizontal(row,col1,col2,board){ for(var i=col1+1;i<col2;i++) if(board[row][i]!=0) return false; return true; } function noBlockVertical(row1,row2,col,board){ for(var i=row1+1;i<row2;i++) if(board[i][col]!=0) return false; return true; } function nomove(){ if(canMoveDown(board)||canMoveRight(board)||canMoveUp(board)||canMoveLeft(board)) return false; return true; }