每个男孩都有一个游戏梦吧,本例简单讲述一款很火的游戏《2048》的制作。
本例参考地址:https://www.imooc.com/learn/76
游戏准备
1、游戏的逻辑(2048大家去玩一玩就知道逻辑了)
2、制作技术:Html,Css,Javascript,Jquery
3、美术
游戏架构
游戏代码
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/index.css"> 7 <script type="text/javascript" src="lib/jquery/jquery.min.js"></script> 8 <script type="text/javascript" src="js/support.js"></script> 9 <script type="text/javascript" src="js/animation.js"></script> 10 <script type="text/javascript" src="js/index.js"></script> 11 </head> 12 <body> 13 <!-- 头部 --> 14 <header> 15 <h1>2048</h1> 16 <a href="javascript:newgame();" id="newgamebutton">New Game</a> 17 <p>score:<span id="score">0</span></p> 18 </header> 19 20 <section id="grid-container"> 21 <div class="grid-cell" id="grid-cell-0-0"></div> 22 <div class="grid-cell" id="grid-cell-0-1"></div> 23 <div class="grid-cell" id="grid-cell-0-2"></div> 24 <div class="grid-cell" id="grid-cell-0-3"></div> 25 <div class="grid-cell" id="grid-cell-1-0"></div> 26 <div class="grid-cell" id="grid-cell-1-1"></div> 27 <div class="grid-cell" id="grid-cell-1-2"></div> 28 <div class="grid-cell" id="grid-cell-1-3"></div> 29 <div class="grid-cell" id="grid-cell-2-0"></div> 30 <div class="grid-cell" id="grid-cell-2-1"></div> 31 <div class="grid-cell" id="grid-cell-2-2"></div> 32 <div class="grid-cell" id="grid-cell-2-3"></div> 33 <div class="grid-cell" id="grid-cell-3-0"></div> 34 <div class="grid-cell" id="grid-cell-3-1"></div> 35 <div class="grid-cell" id="grid-cell-3-2"></div> 36 <div class="grid-cell" id="grid-cell-3-3"></div> 37 38 </section> 39 </body> 40 </html>
css代码:
1 /*头部*/ 2 header { 3 margin: 0 auto; 4 width: 500px; 5 text-align: center; 6 } 7 8 header h1 { 9 font-family: Arial; 10 font-size: 60px; 11 font-weight: bold; 12 } 13 14 header #newgamebutton { 15 display: block; 16 margin: 20px auto; 17 18 width: 100px; 19 padding: 10px; 20 background-color: #8f7a66; 21 22 font-family: Arial; 23 color: white; 24 border-radius: 10px; 25 text-decoration: none; 26 } 27 28 header #newgamebutton:hover { 29 background-color: #9f8b77; 30 } 31 32 header p { 33 font-family: Arial; 34 font-size: 25px; 35 margin: 20px auto; 36 } 37 38 /*格子*/ 39 #grid-container { 40 width: 460px; 41 height: 460px; 42 padding: 20px; 43 44 margin: 50px auto; 45 background-color: #bbada0; 46 47 border-radius: 10px; 48 position: relative; 49 } 50 #grid-container .grid-cell { 51 width: 100px; 52 height: 100px; 53 border-radius: 6px; 54 background-color: #ccc0b3; 55 56 position: absolute; 57 } 58 59 #grid-container .number-cell { 60 border-radius: 6px; 61 62 font-family: Arial; 63 font-weight: bold; 64 font-size: 60px; 65 line-height: 100px; 66 text-align: center; 67 68 position: absolute; 69 }
js部分
底层js:
1 /* 底层支持js */ 2 3 //获取格子距离顶部的距离 4 function getPosTop(i, j) { 5 return 20 + i * 120; 6 } 7 //获取格子距离左边的距离 8 function getPosLeft(i, j) { 9 return 20 + j * 120; 10 } 11 12 //获取格子背景色 13 function getNumberBackgroundColor(number) { 14 switch (number) { 15 case 2: 16 return "#eee4da"; 17 break; 18 case 4: 19 return "#ede0c8"; 20 break; 21 case 8: 22 return "#f2b179"; 23 break; 24 case 16: 25 return "#f59563"; 26 break; 27 case 32: 28 return "#f67c5f"; 29 break; 30 case 64: 31 return "#f65e3b"; 32 break; 33 case 128: 34 return "#edcf72"; 35 break; 36 case 256: 37 return "#edcc61"; 38 break; 39 case 512: 40 return "#9c0"; 41 break; 42 case 1024: 43 return "#33b5e5"; 44 break; 45 case 2048: 46 return "#09c"; 47 break; 48 case 4096: 49 return "#s6c"; 50 break; 51 case 8192: 52 return "#93c"; 53 break; 54 } 55 return "black"; 56 } 57 58 //获取格子文字色 59 function getNumberColor(number) { 60 if (number <= 4) { 61 return "#776e65"; 62 } 63 return "white"; 64 } 65 66 // 判断格子是否有无空余 67 function nospace(board) { 68 for (var row = 0; row < 4; row++) { 69 for (var col = 0; col < 4; col++) { 70 if (board[row][col] == 0) { 71 return false; 72 } 73 } 74 } 75 return true; 76 } 77 78 79 80 //判断能否左移 81 function canMoveLeft(board) { 82 for (var i = 0; i < 4; i++) { 83 84 for (var j = 1; j < 4; j++) { 85 if (board[i][j] != 0) { 86 87 if (board[i][j-1] == 0 || board[i][j-1] == board[i][j]) { 88 // 左侧格子空 或 左侧盒子等于自己 89 return true; 90 } 91 } 92 } 93 } 94 return false; 95 } 96 97 function canMoveRight(board) { 98 for (var i = 0; i < 4; i++) { 99 for (var j = 2; j >= 0; j--) { 100 if (board[i][j] != 0) { 101 if (board[i][j+1] == 0 || board[i][j+1] == board[i][j]) { 102 103 return true; 104 } 105 } 106 } 107 } 108 return false; 109 } 110 111 //判断能否上移 112 function canMoveUp(board) { 113 for (var j = 0; j < 4; j++) { 114 for (var i = 1; i < 4; i++) { 115 if (board[i][j] != 0) { 116 if (board[i-1][j] == 0 || board[i-1][j] == board[i][j]) { 117 118 return true; 119 } 120 } 121 } 122 } 123 return false; 124 } 125 126 127 128 129 function canMoveDown(board) { 130 for (var j = 0; j < 4; j++) { 131 for (var i = 2; i >= 0; i--) { 132 if (board[i][j] != 0) { 133 if (board[i+1][j] == 0 || board[i+1][j] == board[i][j]) { 134 135 return true; 136 } 137 } 138 } 139 } 140 return false; 141 } 142 143 144 145 146 // 判断水平方向上是否有障碍物 147 function noBlockHorizontal(row, col1, col2, board) { 148 for (var i = col1 + 1; i < col2; i++) { 149 if(board[row][i] != 0){ 150 return false; 151 } 152 } 153 return true; 154 } 155 156 // 判断垂直方向是否有障碍物 157 function noBlockVertical(col, row1, row2, board) { 158 for (var i = row1 + 1; i < row2; i++) { 159 if(board[i][col] != 0){ 160 return false; 161 } 162 } 163 return true; 164 } 165 166 167 function nomove(board) { 168 if(canMoveLeft(board) 169 || canMoveRight(board) 170 || canMoveUp(board) 171 || canMoveDown(board)) 172 return false; 173 return true; 174 }
动画js:
// 显示随机数字 function showNumberWithAnimation(randx, randy, randNumber) { var numberCell = $("#number-cell-" + randx + "-" + randy); numberCell.css("background-color", getNumberBackgroundColor(randNumber)); numberCell.css("color", getNumberColor(randNumber)); numberCell.text(randNumber); numberCell.animate({ width: "100px", height: "100px", top: getPosTop(randx, randy), left: getPosLeft(randx, randy) }, 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 updateScore(score) { $("#score").text(score); }
逻辑js:
1 var board = new Array();//全局二维数组 2 var score = 0;//全局分数 3 var hasConflicted = new Array();//全局碰撞二维数组 4 5 //入口 6 $(document).ready(function() { 7 newgame(); 8 }); 9 10 11 //新游戏 12 function newgame() { 13 // 初始化棋盘格 14 init(); 15 // 在随机两个各自生成数字 16 generateOneNumber(); 17 generateOneNumber(); 18 } 19 20 // 初始化棋盘格 21 function init() { 22 23 // 初始位子 24 for (var i = 0; i < 4; i++) { 25 for (var j = 0; j < 4; j++) { 26 var gridCell = $("#grid-cell-" + i + "-" + j); 27 gridCell.css("top", getPosTop(i, j)); 28 gridCell.css("left", getPosLeft(i, j)); 29 } 30 } 31 32 // 初始值 33 for (var i = 0; i < 4; i++) { 34 board[i] = new Array(); 35 hasConflicted[i] = new Array(); 36 for (var j = 0; j < 4; j++) { 37 board[i][j] = 0; 38 hasConflicted[i][j] = false; 39 } 40 } 41 42 // 更新界面显示 43 updateBoardView(); 44 45 score = 0; 46 updateScore(score); 47 48 } 49 50 // 更新界面显示 51 function updateBoardView() { 52 // 移除已有值的元素 53 $(".number-cell").remove(); 54 55 for (var i = 0; i < 4; i++) { 56 for (var j = 0; j < 4; j++) { 57 $("#grid-container").append("<div class='number-cell' id='number-cell-" + i + "-" + j + "'></div>"); 58 var theNumberCell = $("#number-cell-" + i + "-" + j); 59 60 if (board[i][j] == 0) { 61 theNumberCell.css("width", "0px"); 62 theNumberCell.css("height", "0px"); 63 theNumberCell.css("top", getPosTop(i, j) + 50); 64 theNumberCell.css("left", getPosLeft(i, j) + 50); 65 } else { 66 theNumberCell.css("width", "100px"); 67 theNumberCell.css("height", "100px"); 68 theNumberCell.css("top", getPosTop(i, j)); 69 theNumberCell.css("left", getPosLeft(i, j)); 70 theNumberCell.css("background-color", getNumberBackgroundColor(board[i][j])); 71 theNumberCell.css("color", getNumberColor(board[i][j])); 72 theNumberCell.text(board[i][j]); 73 74 75 } 76 hasConflicted[i][j] = false; 77 } 78 } 79 } 80 81 // 随机在格子中生成一个数 82 function generateOneNumber() { 83 84 // 判断格子空间 85 if (nospace(board)) { 86 return false; 87 } 88 89 // 随机一个位子 90 91 var randx = 0; 92 var randy = 0; 93 do { 94 randx = parseInt(Math.floor(Math.random() * 4)); 95 randy = parseInt(Math.floor(Math.random() * 4)); 96 if (board[randx][randy] == 0) break; 97 } 98 while(true); 99 100 // 随机一个数 101 var randNumber = Math.random() < 0.5 ? 2 : 4 ; 102 103 // 在随机位子显示随机数 104 board[randx][randy] = randNumber; 105 showNumberWithAnimation(randx, randy, randNumber); 106 107 return true; 108 109 110 } 111 112 113 114 // 添加操作事件 115 $(document).keydown(function(event){ 116 switch(event.keyCode) { 117 case 37: // left 118 if(moveLeft()) { 119 setTimeout("generateOneNumber()", 210); 120 setTimeout("isgameover()", 300); 121 } 122 console.log(board); 123 break; 124 case 38: // up 125 if(moveUp()) { 126 setTimeout("generateOneNumber()", 210); 127 setTimeout("isgameover()", 300); 128 } 129 break; 130 case 39: // right 131 if(moveRight()) { 132 setTimeout("generateOneNumber()", 210); 133 setTimeout("isgameover()", 300); 134 } 135 break; 136 case 40: // down 137 if(moveDown()) { 138 setTimeout("generateOneNumber()", 210); 139 setTimeout("isgameover()", 300); 140 } 141 break; 142 default: break; 143 } 144 return false; 145 }); 146 147 function isgameover(){ 148 if(nospace(board) && nomove(board)) { 149 gameover(); 150 } 151 } 152 153 function gameover(){ 154 alert("gameover"); 155 } 156 157 // 左移 158 function moveLeft() { 159 // 判断能否左移 160 if(!canMoveLeft(board)) { 161 return false; 162 } 163 164 for (var row = 0; row < 4; row++) { 165 for (var col = 1; col < 4; col++) { 166 if(board[row][col] != 0) { 167 168 for (var k = 0; k < col; k++) { 169 // 左侧为0 170 if(board[row][k] == 0 && noBlockHorizontal(row, k, col, board)){ 171 // move 172 showMoveAnimation(row, col, row, k); 173 board[row][k] = board[row][col]; 174 board[row][col] = 0; 175 continue; 176 } 177 // 左侧等于自己 178 else if (board[row][ k] == board[row][col] && noBlockHorizontal(row, k, col, board) && !hasConflicted[row][k]) 179 { 180 // move 181 showMoveAnimation(row, col, row, k); 182 183 // add 184 board[row][k] += board[row][col]; 185 board[row][col] = 0; 186 187 score += board[row][k]; 188 updateScore(score); 189 190 hasConflicted[row][k] = true; 191 192 continue; 193 } 194 } 195 } 196 } 197 } 198 199 setTimeout("updateBoardView()", 200); 200 return true; 201 } 202 203 204 // 右移 205 function moveRight() { 206 // 判断能否右移 207 if(!canMoveRight(board)) { 208 return false; 209 } 210 211 for (var row = 0; row < 4; row++) { 212 for (var col = 2; col >= 0; col--) { 213 if(board[row][col] != 0) { 214 215 for (var k = 3; k > col; k--) { 216 217 if(board[row][k] == 0 && noBlockHorizontal(row, col, k, board)){ 218 // move 219 showMoveAnimation(row, col, row, k); 220 board[row][k] = board[row][col]; 221 board[row][col] = 0; 222 continue; 223 } 224 225 else if (board[row][k] == board[row][col] && noBlockHorizontal(row, col, k, board) && !hasConflicted[row][k]) 226 { 227 // move 228 showMoveAnimation(row, col, row, k); 229 230 // add 231 board[row][k] += board[row][col]; 232 board[row][col] = 0; 233 234 score += board[row][k]; 235 updateScore(score); 236 237 hasConflicted[row][k] = true; 238 239 continue; 240 } 241 } 242 } 243 } 244 } 245 246 setTimeout("updateBoardView()", 200); 247 return true; 248 } 249 250 251 252 // 上移 253 function moveUp() { 254 // 判断能否上移 255 if(!canMoveUp(board)) { 256 return false; 257 } 258 259 for (var col = 0; col < 4; col++) { 260 for (var row = 1; row < 4; row++) { 261 262 if(board[row][col] != 0) { 263 264 for (var k = 0; k < row; k++) { 265 266 if(board[k][col] == 0 && noBlockVertical(col, k, row, board)){ 267 // move 268 showMoveAnimation(row, col, k, col); 269 board[k][col] = board[row][col]; 270 board[row][col] = 0; 271 continue; 272 } 273 274 else if (board[k][col] == board[row][col] && noBlockVertical(col, k, row, board) && !hasConflicted[k][col]) 275 { 276 // move 277 showMoveAnimation(row, col, k, col); 278 279 // add 280 board[k][col] += board[row][col]; 281 board[row][col] = 0; 282 283 score += board[k][col]; 284 updateScore(score); 285 286 hasConflicted[k][col] = true; 287 288 continue; 289 } 290 } 291 } 292 } 293 } 294 295 setTimeout("updateBoardView()", 200); 296 return true; 297 } 298 299 300 301 302 // 下移 303 function moveDown() { 304 // 判断能否下移 305 if(!canMoveDown(board)) { 306 return false; 307 } 308 309 for (var col = 0; col < 4; col++) { 310 311 for (var row = 2; row >= 0; row--) { 312 if(board[row][col] != 0) { 313 314 for (var k = 3; k > row; k--) { 315 316 if(board[k][col] == 0 && noBlockVertical(col, row, k, board)){ 317 // move 318 showMoveAnimation(row, col, k, col); 319 board[k][col] = board[row][col]; 320 board[row][col] = 0; 321 continue; 322 } 323 324 else if (board[k][col] == board[row][col] && noBlockVertical(col, row, k, board) && !hasConflicted[k][col]) 325 { 326 // move 327 showMoveAnimation(row, col, k, col); 328 329 // add 330 board[k][col] += board[row][col]; 331 board[row][col] = 0; 332 333 score += board[k][col] 334 updateScore(score); 335 336 hasConflicted[k][col] = true; 337 continue; 338 } 339 } 340 } 341 } 342 } 343 344 setTimeout("updateBoardView()", 200); 345 return true; 346 }
游戏展示
普通版地址:http://naughty7878.top/game/2048
优化版地址:http://naughty7878.top/game/2048plus
游戏优化
1、随机数的参数,怎么更加准确的找空空位,在随机空位中产生随机数
2、增加分数时,增加动画。
3、游戏结束时,动画结束
4、私人定制游戏,显示时:2-->小白,4-->实习生,8 -->程序猿,16-->项目经理
5、操作问题,怎么鼠标操作,触摸滑动操作
6、设备适配的问题,兼容手机等。