JavaScript实现简单贪吃蛇小游戏
之前上Web课,学到JavaScript的时候,老师要求写几个静态页面,要用到JavaScript。想了想就写个贪吃蛇吧。其实之前用pygame写过一次GUI的贪吃蛇,素材都是自己拿画图画的,其丑无比。所以这次还是老老实实用字符吧。
首先,是一些全局变量的定义:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <script> 2 var state = 0;//0 wait 1 run 2 over 3 var width = 40; 4 var height = 25; 5 var update = false; 6 var dir = 3; 7 var ndir = 3; 8 //0 top 1 down 2 left 3 right 9 //l 37 r 39 u 38 d 40 space 32 10 11 var waittime = 50; 12 13 //△▽○◇□☆●◆■★〼◎¤۞卍卐 14 var snakebody="■";//■ 15 var food = "★";//★ 16 var foodX; 17 var foodY; 18 var space = "□"; 19 var snakeX; 20 var snakeY; 21 var length; 22 23 var map; 24 25 var drawMap; 26 27 function debugInfo(str) 28 { 29 //document.getElementById("debug").innerHTML=str; 30 console.log(str); 31 } 32 33 ... 34 </script>
在这里定义了游戏的状态(等待、运行、结束),其实也可以简化成只有等待和运行,不过为了逻辑上通顺再方便拓展还是这样好一点。width和height是游戏界面的大小。update本来设计是与游戏循环有关,但是实际上没用到。dir、ndir是控制蛇的移动,也在后面说。snakebody、food、space地图上会出现的三种形状,就是蛇、食物、空地。foodX、foodY是食物的位置,snakeX、snakeY则表示蛇,二者都是个数组,length就表示当前蛇的长度。map是当前地图,drawMap本来打算用来辅助渲染,最后还是没用到。debugInfo函数嘛,当时我还不知道有console.log()这玩意儿,就自己写了这样一个函数,用来在页面上显示错误信息。
然后是游戏的初始化:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
function Init() { dir = 3; length = 2; map = new Array(width); for(var i = 0; i < width; ++i) { map[i] = new Array(height); for(var j = 0; j < height; ++j) map[i][j] = space; } snakeX = new Array(width * height); snakeY = new Array(width * height); for(i = 0; i < width * height; ++i) { snakeX[i] = 0; snakeX[i] = 0; } snakeX[1] = 0; snakeX[0] = 0; snakeY[0] = 1; snakeY[0] = 0; map[1][0] = snakebody; map[0][0] = snakebody; document.getElementById("msg").innerHTML="Press Space Start"; Draw(); }
在Init函数中完成游戏的初始化。默认蛇一开始长度为2,位于地图的左上角,然后方向朝右。然后把map new成一个二维数组。蛇坐标x、y new成两个数组,大小就是地图的大小。然后调用一次Draw画出默认的样子。Init函数一开始会放在页面的onload。
游戏的渲染部分:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
function Draw() { var str = ""; for(i = 0; i < height; ++i) { for(j = 0; j < width; ++j) { str += map[j][i]; } str += "<br/>"; } document.getElementById("gameArea").innerHTML=str; }
就只是简单的把map变成一个字符串放到页面上。
游戏的开始和结束:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 function Start() 2 { 3 state = 1; 4 update=true; 5 SpawnFood(); 6 Update(); 7 } 8 9 function dead() 10 { 11 state = 2; 12 document.getElementById("msg").innerHTML="Your Score: " + length + ", Press Space to ReStart."; 13 }
游戏开始,变更状态为运行,生成食物并开始更新游戏。游戏结束,变更状态为结束,显示分数。
生成食物:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 function SpawnFood() 2 { 3 loop = true; 4 while(loop) 5 { 6 foodX = parseInt(Math.random() * width); 7 foodY = parseInt(Math.random() * height); 8 for(i = 0; i < length; ++i) 9 { 10 if(foodX == snakeX[i] && foodY == snakeY[i]) 11 {} 12 else 13 loop = false; 14 } 15 } 16 }
生成食物就直接随机一个坐标,检查一下跟蛇的身体有没有重合,毕竟食物要出现在一个空地啊,免得蛇太长的时候可能走半天都不知道食物在哪。
主循环:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 function Update() 2 { 3 //0 top 1 down 2 left 3 right 4 var nextx = snakeX[0]; 5 var nexty = snakeY[0]; 6 dir = ndir; 7 if(dir == 0) 8 { 9 --nexty; 10 } 11 else if(dir == 1) 12 { 13 ++nexty; 14 } 15 else if(dir == 2) 16 { 17 --nextx; 18 } 19 else if(dir == 3) 20 { 21 ++nextx; 22 } 23 for(i = 0; i < length; ++i) 24 { 25 if(nextx == snakeX[i] && snakeY[i] == nexty) 26 { 27 debugInfo("Eat Self" + nextx + "," + nexty + "<br/>head" + snakeX[0] + "," + snakeY[0]); 28 dead(); 29 return; 30 } 31 } 32 33 if(nextx < 0 || nextx >= width || nexty < 0 || nexty >= height) 34 { 35 debugInfo("Eat Wall"); 36 dead(); 37 return; 38 } 39 40 if(nextx == foodX && nexty == foodY) 41 { 42 ++length; 43 snakeX.unshift(nextx); 44 snakeY.unshift(nexty); 45 map[foodX][foodY] = snakebody; 46 SpawnFood(); 47 } 48 else 49 { 50 snakeX.unshift(nextx); 51 snakeY.unshift(nexty); 52 lastx = snakeX[length]; 53 lasty = snakeY[length]; 54 //debugInfo("Next Pos:" + nextx + "," + nexty + "<br/>Last Pos: " + lastx + "," + lasty); 55 map[lastx][lasty] = space; 56 map[nextx][nexty] = snakebody; 57 } 58 59 60 61 map[foodX][foodY] = food; 62 debugInfo("food Pos:" + foodX + "," + nexty); 63 Draw(); 64 65 66 document.getElementById("msg").innerHTML="Your Score: " + length; 67 setTimeout("Update()", waittime); 68 }
Update函数是游戏的主循环。本来在Windows程序中他是要放在一个while(true)循环当中的,JS不太一样,想了想用setTimeout以设定好的速率更新游戏。
在update函数中首先对蛇进行了移动。蛇的移动很简单,根据当前蛇头的位置(也就是蛇坐标(snakeX[0], snakeY[0]) )和当前爬行方向算出接下来到达的位置(nextx,nexty)。首先检查这个位置是否是蛇身体某一部分,如果是,那么表明他吃到自己了,游戏结束。然后检查这个位置是否超出我们设置的地图大小了,如果是,那么蛇撞墙,游戏结束。如果都没有,说明移动是正常的,然后检查这个位置是否是食物的位置。如果是,说明吃到食物,直接把这个坐标放到蛇坐标数组的最前面,也就是蛇头,蛇的长度+1,然后生成新的食物。否则,也把这个坐标放到蛇坐标数组的最前面,然后把蛇的最末端去掉。这样就完成了蛇的移动。
之后调用Draw更新页面,并显示当前分数。
输入处理:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 function keyDown() 2 { 3 //alert("You Press " + event.keyCode); 4 //l 37 r 39 u 38 d 40 space 32 5 if(state == 0) 6 { 7 //wait 8 if(event.keyCode == 32) 9 { 10 Start(); 11 } 12 } 13 if(state == 1) 14 { 15 //0 top 1 down 2 left 3 right 16 //l 37 r 39 u 38 d 40 space 32 17 if(event.keyCode == 37 && dir != 3) 18 { 19 ndir = 2; 20 debugInfo("Change Dir to left"); 21 } 22 else if(event.keyCode == 39 && dir != 2) 23 { 24 ndir = 3; 25 debugInfo("Change Dir to right"); 26 } 27 else if(event.keyCode == 38 && dir != 1) 28 { 29 ndir = 0; 30 debugInfo("Change Dir to top"); 31 } 32 else if(event.keyCode == 40 && dir != 0) 33 { 34 ndir = 1; 35 debugInfo("Change Dir to down"); 36 } 37 } 38 if(state == 2) 39 { 40 if(event.keyCode == 32) 41 { 42 Init(); 43 Start(); 44 } 45 } 46 } 47 document.onkeydown = keyDown;
这里定义了一个keydown函数处理键盘输入,用document.onkeydown = keyDown;把他作为页面的keydown事件处理函数。
在这个键盘处理程序里,首先判断游戏状态。等待状态下按下空格键(keyCode:32),调用start函数游戏开始。游戏结束状态下按下空格键,调用init和start函数,游戏重开。游戏运行状态下,按下四个方向键,改变蛇爬行方向。
于是,这就是贪吃蛇全部JavaScript代码了。当然还有页面和样式,这是全部代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <html> 2 3 <head> 4 <title>贪吃蛇</title> 5 <meta charset="utf-8"> 6 7 <style> 8 #msg 9 { 10 color:#ff0000; 11 } 12 h1 13 { 14 text-align: center; 15 color: #ffffff; 16 font-size: 64px; 17 font-family: "Consolas"; 18 } 19 body 20 { 21 22 background-color: #dddddd; 23 } 24 table 25 { 26 border: solid 1px, #66ccff; 27 } 28 td 29 { 30 border: solid 1px, #66ccff; 31 } 32 </style> 33 34 <script type="text/javascript"> 35 var state = 0;//0 wait 1 run 2 over 36 var width = 40; 37 var height = 25; 38 var update = false; 39 var dir = 3; 40 var ndir = 3; 41 //0 top 1 down 2 left 3 right 42 //l 37 r 39 u 38 d 40 space 32 43 44 var waittime = 50; 45 46 //△▽○◇□☆●◆■★〼◎¤۞卍卐 47 var snakebody="■";//■ 48 var food = "★";//★ 49 var foodX; 50 var foodY; 51 var space = "□"; 52 var snakeX; 53 var snakeY; 54 var length; 55 56 var map; 57 58 var drawMap; 59 60 function debugInfo(str) 61 { 62 //document.getElementById("debug").innerHTML=str; 63 console.log(str); 64 } 65 66 function Draw() 67 { 68 var str = ""; 69 for(i = 0; i < height; ++i) 70 { 71 for(j = 0; j < width; ++j) 72 { 73 str += map[j][i]; 74 } 75 str += "<br/>"; 76 } 77 78 document.getElementById("gameArea").innerHTML=str; 79 } 80 81 function dead() 82 { 83 state = 2; 84 document.getElementById("msg").innerHTML="Your Score: " + length + ", Press Space to ReStart."; 85 } 86 87 function Update() 88 { 89 //0 top 1 down 2 left 3 right 90 var nextx = snakeX[0]; 91 var nexty = snakeY[0]; 92 dir = ndir; 93 if(dir == 0) 94 { 95 --nexty; 96 } 97 else if(dir == 1) 98 { 99 ++nexty; 100 } 101 else if(dir == 2) 102 { 103 --nextx; 104 } 105 else if(dir == 3) 106 { 107 ++nextx; 108 } 109 110 for(i = 0; i < length; ++i) 111 { 112 if(nextx == snakeX[i] && snakeY[i] == nexty) 113 { 114 debugInfo("Eat Self" + nextx + "," + nexty + "<br/>head" + snakeX[0] + "," + snakeY[0]); 115 dead(); 116 return; 117 } 118 } 119 120 if(nextx < 0 || nextx >= width || nexty < 0 || nexty >= height) 121 { 122 debugInfo("Eat Wall"); 123 dead(); 124 return; 125 } 126 127 if(nextx == foodX && nexty == foodY) 128 { 129 ++length; 130 snakeX.unshift(nextx); 131 snakeY.unshift(nexty); 132 map[foodX][foodY] = snakebody; 133 SpawnFood(); 134 } 135 else 136 { 137 snakeX.unshift(nextx); 138 snakeY.unshift(nexty); 139 lastx = snakeX[length]; 140 lasty = snakeY[length]; 141 //debugInfo("Next Pos:" + nextx + "," + nexty + "<br/>Last Pos: " + lastx + "," + lasty); 142 map[lastx][lasty] = space; 143 map[nextx][nexty] = snakebody; 144 } 145 146 147 148 map[foodX][foodY] = food; 149 debugInfo("food Pos:" + foodX + "," + nexty); 150 Draw(); 151 152 153 document.getElementById("msg").innerHTML="Your Score: " + length; 154 setTimeout("Update()", waittime); 155 } 156 157 function SpawnFood() 158 { 159 loop = true; 160 while(loop) 161 { 162 foodX = parseInt(Math.random() * width); 163 foodY = parseInt(Math.random() * height); 164 for(i = 0; i < length; ++i) 165 { 166 if(foodX == snakeX[i] && foodY == snakeY[i]) 167 {} 168 else 169 loop = false; 170 } 171 } 172 } 173 174 function Start() 175 { 176 state = 1; 177 update=true; 178 SpawnFood(); 179 Update(); 180 } 181 182 function Init() 183 { 184 dir = 3; 185 length = 2; 186 187 map = new Array(width); 188 for(var i = 0; i < width; ++i) 189 { 190 map[i] = new Array(height); 191 192 for(var j = 0; j < height; ++j) 193 map[i][j] = space; 194 } 195 196 snakeX = new Array(width * height); 197 snakeY = new Array(width * height); 198 for(i = 0; i < width * height; ++i) 199 { 200 snakeX[i] = 0; 201 snakeX[i] = 0; 202 } 203 snakeX[1] = 0; 204 snakeX[0] = 0; 205 snakeY[0] = 1; 206 snakeY[0] = 0; 207 map[1][0] = snakebody; 208 map[0][0] = snakebody; 209 210 document.getElementById("msg").innerHTML="Press Space Start"; 211 Draw(); 212 } 213 function keyDown() 214 { 215 //alert("You Press " + event.keyCode); 216 //l 37 r 39 u 38 d 40 space 32 217 if(state == 0) 218 { 219 //wait 220 if(event.keyCode == 32) 221 { 222 Start(); 223 } 224 } 225 if(state == 1) 226 { 227 //0 top 1 down 2 left 3 right 228 //l 37 r 39 u 38 d 40 space 32 229 if(event.keyCode == 37 && dir != 3) 230 { 231 ndir = 2; 232 debugInfo("Change Dir to left"); 233 } 234 else if(event.keyCode == 39 && dir != 2) 235 { 236 ndir = 3; 237 debugInfo("Change Dir to right"); 238 } 239 else if(event.keyCode == 38 && dir != 1) 240 { 241 ndir = 0; 242 debugInfo("Change Dir to top"); 243 } 244 else if(event.keyCode == 40 && dir != 0) 245 { 246 ndir = 1; 247 debugInfo("Change Dir to down"); 248 } 249 } 250 if(state == 2) 251 { 252 if(event.keyCode == 32) 253 { 254 Init(); 255 Start(); 256 } 257 } 258 259 } 260 document.onkeydown = keyDown; 261 </script> 262 263 </head> 264 265 <body onload="Init()"> 266 <table> 267 <tr> 268 <td> 269 <h1 id="title">贪吃蛇</h1> 270 </td> 271 </tr> 272 <tr> 273 <td> 274 <span id="debug"> 275 </span> 276 </td> 277 </tr> 278 <tr> 279 <td align=center> 280 <span id="msg"> 281 </span> 282 </td> 283 <tr> 284 <tr> 285 <td> 286 <span id="gameArea"> 287 </span> 288 </td> 289 </tr> 290 </table> 291 </body> 292 293 </html>
运行截图如下: