一、主要思路
1、游戏地图创建:使用table生成地图,地图大小可调;
2、蛇和食物的随机生成:蛇使用对象数组来维护蛇的身体,食物采用随机生成的{"x":x,"y":y}。
3、蛇行动和控制相关:keydown事件添加;
4、失败条件判断:撞墙失败和咬自己失败;
二、重点
1、地图、蛇、食物的一格对应的是{"x":x,"y":y}的对象。这样子就可以通过坐标的加减判断蛇的移动;
2、蛇、食物的移动“渲染”,暂且叫“渲染”,这个说法很不规范。比如30*30格的地图,对应的每个对象“0,0”、“0,1”……“0,29”其实就是一格30进制的数,而且只有两位,这样不必使用循环就可以“渲染”出蛇和食 物;
三、效果
四、代码
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <title> 贪吃蛇(面向对象版)</title> 6 <style type="text/css"> 7 html,body{overflow:hidden;} 8 body,div,p{margin:0;padding:0;} 9 body{background:#000;font:12px/1.5 arial;color:#7A7A7A;} 10 a{text-decoration:none;outline:none;} 11 #tips,#copyright{position:absolute;width:100%;height:50px;text-align:center;background:#171717;border:2px solid #484848;} 12 #tips{top:0;border-width:0 0 2px;} 13 #tips a{font:14px/30px arial;color:#FFF;background:#F06;display:inline-block;margin:10px 5px 0;padding:0 15px;border-radius:15px;} 14 #tips a.active{background:#FE0000;} 15 #copyright{bottom:0;line-height:50px;border-width:2px 0 0;} 16 #copyright a{color:#FFF;background:#7A7A7A;padding:2px 5px;border-radius:10px;} 17 #copyright a:hover{background:#F90;} 18 #box{background:#eee;width: 100%;height: 720px;margin: 50px 0px;} 19 p{position:absolute;top:55px;width:100%;text-align:center;} 20 #box #panle{width: 100%;height: 93%;padding-top: 30px;} 21 #panle table{border-collapse: collapse; margin: 0 auto;} 22 #panle table td{ 23 border: 1px solid #808080; 24 width: 10px; 25 height: 10px; 26 font-size: 0; 27 line-height: 0; 28 overflow: hidden; } 29 .food{background: green;} 30 .snake{background: #555;} 31 </style> 32 <script type="text/javascript"> 33 var fgm = { 34 on: function(element, type, handler) { 35 return element.addEventListener ? element.addEventListener(type, handler, false) : element.attachEvent("on" + type, handler) 36 }, 37 un: function(element, type, handler) { 38 return element.removeEventListener ? element.removeEventListener(type, handler, false) : element.detachEvent("on" + type, handler) 39 }, 40 bind: function(object, handler) { 41 return function() { 42 return handler.apply(object, arguments) 43 } 44 }, 45 randomRange: function(lower, upper) { 46 return Math.floor(Math.random() * (upper - lower + 1) + lower) 47 }, 48 getRanColor: function() { 49 var str = this.randomRange(0, 0xFFFFFF).toString(16); 50 while(str.length < 6) str = "0" + str; 51 return "#" + str 52 }, 53 has:function(a,arry){ 54 var flag = false; 55 for(var i = 0; i < arry.length; i++){ 56 if(fgm.equal(a,arry[i])){ 57 flag = true; 58 } 59 } 60 return flag; 61 }, 62 equal:function(a,b) 63 { 64 return a.x == b.x && a.y == b.y; 65 }, 66 cast:function(m,n) 67 { 68 // m进制 n 待转换参数 69 if(typeof(n) === "object"){ 70 // 转换成10进制 71 return n.x * m + n.y; 72 }else{ 73 // 转换成m进制 74 var y = n % m; 75 var x = (n/m) % m; 76 return {"x":x,"y":y} 77 } 78 } 79 }; 80 //初始化对象 81 function Snake() { 82 this.map = 30; // 地图大小 83 this.speed = 500; // 速度 84 this.fnInit = fgm.bind(this, this.initialize) 85 } 86 Snake.prototype = { 87 initialize:function(obj) 88 { 89 this.oParent = obj; 90 this.space = []; 91 this.type = 0; //0暂停 1开始 2失败 92 this.timer = null; 93 this.score = 0; // 速度 94 this.direction = 3; // 0 1 2 3 - 上 下 左 右 默认开始向右跑 95 this.snake = [{"x":0,"y":0}]; // 蛇身体 96 this.food = null; // 食物 97 this.__create__(); 98 }, 99 __create__:function() 100 { 101 var oThis = this; 102 this.tab = this.oParent.getElementsByTagName("table")[0]; 103 this.tab && (this.oParent.removeChild(this.tab)); 104 this.tab = document.createElement("table"); 105 var oFrag = document.createDocumentFragment(); 106 var i = 0; 107 for(i = 0; i < this.map; i++) 108 { 109 var oTr = document.createElement("tr"); 110 for(var j = 0; j < oThis.map; j++) 111 { 112 var oTd = document.createElement("td"); 113 oTr.appendChild(oTd); 114 this.space.push({"x":i,"y":j,"obj":oTd}); 115 } 116 oFrag.appendChild(oTr); 117 } 118 this.tab.appendChild(oFrag); 119 this.oParent.appendChild(this.tab); 120 this.td = this.tab.getElementsByTagName("td"); 121 this.food = this.randomFood(); 122 this.changeSnake(); 123 this.changeFood(); 124 }, 125 start:function() 126 { 127 var oThis = this; 128 fgm.on(document,"keydown",function(event){ 129 var e = event || window.event; 130 switch(e.keyCode) 131 { 132 case 37: // left 2 133 if(oThis.direction == 0 || oThis.direction == 1){ 134 oThis.direction = 2; 135 } 136 break; 137 case 38: // up 0 138 if(oThis.direction == 2 || oThis.direction == 3){ 139 oThis.direction = 0; 140 } 141 break; 142 case 39: // right 3 143 if(oThis.direction == 0 || oThis.direction == 1){ 144 oThis.direction = 3; 145 } 146 break; 147 case 40: // down 1 148 if(oThis.direction == 2 || oThis.direction == 3){ 149 oThis.direction = 1; 150 } 151 break; 152 } 153 //console.log(oThis.direction) 154 }); 155 oThis.doMove(this.speed); 156 }, 157 doMove:function(ispeed,fn) 158 { 159 clearInterval(this.timer); 160 var oThis = this; 161 this.timer = setInterval(function(){ 162 var next = oThis.nextFn(); 163 if(fgm.equal(next,oThis.food)){ 164 oThis.snake.unshift(oThis.food); 165 oThis.food = oThis.randomFood(); 166 oThis.changeFood(); 167 oThis.changeSnake(); 168 next = oThis.nextFn(); 169 oThis.score++; 170 } 171 oThis.faild(next) && (clearInterval(oThis.timer),alert("GAME OVER YOUR SCORE IS "+oThis.score)); 172 oThis.snake.unshift(next); 173 oThis.snake.pop(); 174 oThis.changeSnake(); 175 },oThis.speed) 176 }, 177 faild:function(a) 178 { 179 var wall = false; 180 var bite = false; 181 switch(this.direction) 182 { 183 case 3: 184 a.y >= this.map && (wall = true); 185 break; 186 case 1: 187 a.x >= this.map && (wall = true); 188 break; 189 case 2: 190 a.y < 0 && (wall = true); 191 break; 192 case 0: 193 a.x < 0 && (wall = true); 194 break; 195 } 196 bite = fgm.has(a,this.snake); 197 return wall || bite; 198 }, 199 nextFn:function() 200 { 201 var x,y = 0; 202 var oThis = this; 203 this.direction == 3 &&( x = this.snake[0].x ,y = this.snake[0].y + 1); 204 this.direction == 1 &&( x = this.snake[0].x + 1 ,y = this.snake[0].y); 205 this.direction == 2 &&( x = this.snake[0].x ,y = this.snake[0].y - 1); 206 this.direction == 0 &&( x = this.snake[0].x - 1 ,y = this.snake[0].y); 207 var a = {"x":x,"y":y}; 208 return a; 209 }, 210 randomFood:function() 211 { 212 var a = {"x":fgm.randomRange(1,this.map-1),"y":fgm.randomRange(1,this.map-1)}; 213 if(fgm.has(a,this.snake)){ 214 this.food(); 215 }else{ 216 return a; 217 } 218 }, 219 changeSnake:function() 220 { 221 var oThis = this; 222 for(var i = 0; i < this.space.length; i++)this.space[i].obj.className = ""; 223 for(var i = 0; i < this.snake.length; i++){ 224 this.space[fgm.cast(oThis.map,oThis.snake[i])].obj.className = "snake"; 225 } 226 this.changeFood(); 227 }, 228 changeFood:function() 229 { 230 this.space[fgm.cast(this.map,this.food)].obj.className = "food"; 231 }, 232 stop:function() 233 { 234 this.timer && clearInterval(this.timer); 235 } 236 }; 237 238 fgm.on(window, "load", function() { 239 var oTips = document.getElementById("tips"); 240 var aBtn = oTips.getElementsByTagName("a"); 241 var select = oTips.getElementsByTagName("select"); 242 var panle = document.getElementById("panle"); 243 var snake = new Snake(); 244 fgm.on(oTips, "click", function(event) { 245 var oEvent = event || window.event; 246 var oTarget = oEvent.target || oEvent.srcElement; 247 var i = 0; 248 if(oTarget.tagName.toUpperCase() == "A") { 249 for(i = 0; i < aBtn.length; i++) aBtn[i].className = ""; 250 switch(oTarget.id) { 251 case "start": 252 snake.type = 1; 253 snake.start(); 254 for(var j = 0; j < select.length; j++){ 255 select[j].disabled = true; 256 } 257 break; 258 case "stop": 259 snake.type = 0; 260 snake.stop(); 261 break; 262 } 263 oTarget.className = "active"; 264 oEvent.stopPropagation ? oEvent.stopPropagation() : oEvent.cancelBubble = true 265 } 266 }); 267 fgm.on(select[0],"change",function(){ 268 snake.map = select[0].value; 269 snake.initialize(panle); 270 }); 271 fgm.on(select[1],"change",function(){ 272 snake.speed = select[1].value; 273 snake.initialize(panle); 274 }); 275 snake.initialize(panle); 276 }); 277 fgm.on(document, "contextmenu", function(event) { 278 var oEvent = event || window.event; 279 oEvent.preventDefault ? oEvent.preventDefault() : oEvent.returnValue = false 280 }); 281 </script> 282 </head> 283 <body> 284 <div id="tips"><a id="start" href="javascript:;">开始游戏</a><a id="stop" href="javascript:;">停止游戏</a> 285 <select id="map"> 286 <option value = "30">30 * 30</option> 287 <option value = "40">40 * 40</option> 288 <option value = "50">50 * 50</option> 289 </select> 290 <select id="speed"> 291 <option value = "500">速度 - 慢</option> 292 <option value = "300">速度 - 中</option> 293 <option value = "200">速度 - 快</option> 294 </select> 295 <p>游戏使用键盘“上下左右”控制,点击开始游戏开始</p> 296 </div> 297 298 <div id="box"> 299 300 <div id="panle"> 301 </div> 302 </div> 303 <div id="copyright">建议使用Firefox, Chrome浏览器预览效果. 如蒙转载请注明出处 <a href="http://weibo.com/3839963356/profile?topnav=1&wvr=6&is_all=1">winter</a> , By — ben, QQ:1583145833</div> 304 </body> 305 </html>