代码随便写写,尚有许多不足,PC与手机端皆可运行
手机端滑屏操作,PC端方向键操作
疑问:
生成食物,与判断是否可以移动方面
有两种实现方式,
1.使用js内存,数组循环判断
2.使用dom的query方法
哪种比较快,哪种比较好?
目前的代码是用第二种方法实现
在线地址:
http://wangxinsheng.herokuapp.com/snake
截图:
部分代码:
html:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <meta name="description" content="html5 snake game"> 6 <meta name="keywords" content="snake,html5,canvas,web,game"> 7 <meta name="author" content="WangXinsheng"> 8 <meta name="apple-mobile-web-app-capable" content="yes"> 9 <meta name="apple-mobile-web-app-status-bar-style" content="black"> 10 <meta name="viewport" id="viewport" content="width = device-width, initial-scale = 1, minimum-scale = 1, maximum-scale = 1, user-scalable=no"> 11 <meta http-equiv="X-UA-Compatible" content="chrome=1"> 12 <meta http-equiv="Pragma" content="no-cache"> 13 <meta http-equiv="Cache-Control" content="no-cache"> 14 <meta equiv="Expires" content="0"> 15 <meta http-equiv="content-script-type" content="text/javascript"> 16 <title>CopyRight©WangXinsheng</title> 17 <script src="game/requestNextAnimationFrame.js"></script> 18 <style type="text/css"> 19 html {color:#000;background:gray;margin:0px; overflow:hidden;} 20 body {-webkit-user-select:none;margin:0px;} 21 #gameWorld {background-color:#C6EEC6} 22 #opp{color:red; font-size:30px; font-weight:bold; text-align:center;vertical-align:middle; cursor:pointer;} 23 </style> 24 </head> 25 <body> 26 <canvas id="gameWorld" style="position: absolute; left: 0px; top: 0px;"> 27 <p>You need a modern browser to view this.</p> 28 </canvas> 29 <div id="opp" style="position: absolute; left: 0px; top: 0px;">点击开始游戏</div> 30 <ul style='display:none' id='dataDom'></ul> 31 </body> 32 <script src="game/snakeGame_min.js"></script> 33 </html>
js使用dom方法:
1.初期往页面放入dom
1 /*put data dom*/ 2 var dataDom = document.getElementById("dataDom"); 3 // document.querySelector("#dataDom"); 4 var domInner = ""; 5 for(var i = 1;i<=blockWNum;i++){ 6 for(var j = 1;j<=blockHNum;j++){ 7 domInner += liStr 8 .replace("{%x%}",i) 9 .replace("{%y%}",j) 10 .replace("{%s%}","0"); 11 } 12 } 13 dataDom.innerHTML =domInner; //domInner;
2.判断是否撞到自己
1 result = 2 ("1"==document.querySelector("#dataDom [data-x='"+nx+"'][data-y='"+ny+"']").getAttribute("data-s")) 3 ?false 4 :true;
3.蛇移动:1)移动目的地作为头部插入蛇身体。2)删除蛇尾部
1 switch(dir){ 2 case "N": 3 this.body.splice(0,0,{"x":this.body[0].x,"y":this.body[0].y-1}); 4 break; 5 case "S": 6 this.body.splice(0,0,{"x":this.body[0].x,"y":this.body[0].y+1}); 7 break; 8 case "W": 9 this.body.splice(0,0,{"x":this.body[0].x-1,"y":this.body[0].y}); 10 break; 11 case "E": 12 this.body.splice(0,0,{"x":this.body[0].x+1,"y":this.body[0].y}); 13 break; 14 default:; 15 } 16 this.setSnakeSts(this.body[0].x,this.body[0].y,1); 17 document.querySelector("#dataDom [data-x='"+x+"'][data-y='"+y+"']").setAttribute("data-s",s);
1 this.setSnakeSts(this.body[this.body.length-1].x,this.body[this.body.length-1].y,0); 2 this.body.splice(this.body.length-1,1);
4.事件绑定
1 if(!v){ 2 document.addEventListener("keyup", onKeyPress, false); 3 }else{ 4 caObj.addEventListener("touchstart", onTouchStart, false); 5 caObj.addEventListener("touchmove", onTouchMove, false); 6 caObj.addEventListener("touchend", onTouchEnd, false); 7 } 8 window.addEventListener("resize", doSize, false);
逻辑流程:
1.初期化canvas大小,位置和蛇身体单格大小
2.生成蛇,食物
3.蛇动作:可否移动,移动,吃食
4.循环动画[定期生成食物,移动操作蛇]
这个非常简单,js完整源码:
1 ; 2 var debug = true; 3 var gameWorld = function () { 4 /*function init params*/ 5 function doVarInit() { 6 //started = true; 7 if(debug) 8 console.log("hello snake powered by WangXinsheng"); 9 } 10 /*function on resize the window*/ 11 function doSize() { 12 // should start again 13 var minLength = window.innerWidth>window.innerHeight 14 ?window.innerHeight 15 :window.innerWidth, 16 maxLength = window.innerWidth<window.innerHeight 17 ?window.innerHeight 18 :window.innerWidth; 19 blockW = Math.floor(minLength / blockMinNum); 20 var caMin = blockW * blockMinNum; 21 var caMax = Math.floor(maxLength / blockW) * blockW; 22 23 caW = window.innerWidth>window.innerHeight 24 ?caMax 25 :caMin; 26 caH = window.innerWidth<window.innerHeight 27 ?caMax 28 :caMin; 29 30 blockWNum = caW / blockW; 31 blockHNum = caH / blockW; 32 33 var top = (window.innerHeight - caH) * 0.5, 34 left = (window.innerWidth - caW) * 0.5; 35 36 //if(debug) 37 //console.log(blockWNum,blockHNum,top,left);// 38 39 caObj.width = caW; 40 caObj.height = caH; 41 caObj.style.width = caW + "px"; 42 caObj.style.height = caH + "px"; 43 caObj.style.left = left + "px"; 44 caObj.style.top = top + "px"; 45 oppDiv.width = caW; 46 oppDiv.height = caH; 47 oppDiv.style.width = caW + "px"; 48 oppDiv.style.height = caH + "px"; 49 oppDiv.style.left = left + "px"; 50 oppDiv.style.top = top + "px"; 51 oppDiv.style.lineHeight = caH + "px"; 52 53 54 /*put data dom*/ 55 var dataDom = document.getElementById("dataDom"); 56 // document.querySelector("#dataDom"); 57 var domInner = ""; 58 for(var i = 1;i<=blockWNum;i++){ 59 for(var j = 1;j<=blockHNum;j++){ 60 domInner += liStr 61 .replace("{%x%}",i) 62 .replace("{%y%}",j) 63 .replace("{%s%}","0"); 64 } 65 } 66 dataDom.innerHTML =domInner; //domInner; 67 //var oldDom = document.querySelector("#dataDom"); 68 //removeDom(oldDom); 69 70 lastFpsUpdateTime = (+new Date); 71 lastFpsUpdateTimeFood = (+new Date); 72 } 73 function setSnakeSts(x,y,s){ 74 document.querySelector("#dataDom [data-x='"+x+"'][data-y='"+y+"']").setAttribute("data-s",s); 75 } 76 function gen(name) { 77 /*product object*/ 78 switch(name){ 79 case "snake": 80 var body = []; 81 body.splice(0,0,{"x":1,"y":1}); 82 body.splice(0,0,{"x":2,"y":1}); 83 body.splice(0,0,{"x":3,"y":1}); 84 body.splice(0,0,{"x":4,"y":1}); 85 setSnakeSts(1,1,1); 86 setSnakeSts(2,1,1); 87 setSnakeSts(3,1,1); 88 setSnakeSts(4,1,1); 89 90 snakeObj = new snake(snakeColor,true,"E",blockW,initSpeed,body); 91 snakeObj.draw(caCt); 92 break; 93 case "food": 94 var allFood = document.querySelectorAll("#dataDom [data-s='0']"), 95 foodX,foodY,index; 96 if(allFood.length==1){ 97 index = 0; 98 }else if(allFood.length>1){ 99 index = Math.round(Math.random()*(allFood.length-1)); 100 } 101 if(allFood.length>=1){ 102 foodX = parseInt(allFood[index].getAttribute("data-x")); 103 foodY = parseInt(allFood[index].getAttribute("data-y")); 104 allFood[index].getAttribute("data-s","2"); 105 foodObj.x = foodX; 106 foodObj.y = foodY; 107 foodObj.live = true; 108 } 109 drawFood(); 110 break; 111 default: 112 } 113 } 114 function picIsLoaded() { 115 return true; 116 } 117 function loadPics() { 118 /*load pic to objectList*/ 119 } 120 function loadedImg() { 121 /*if image loaded do here*/ 122 } 123 function drawObject(name){ 124 /*draw object to canvas*/ 125 } 126 function animate(time) { 127 if (started) { 128 var now = (+new Date); 129 if (now - lastFpsUpdateTime > snakeObj.speed) { 130 /*animate*/ 131 if(snakeObj.canMove(snakeObj.dir,blockWNum,blockHNum)){ 132 if(snakeObj.canEat(snakeObj.dir,foodObj)){ 133 snakeObj.doEat(snakeObj.dir,foodObj); 134 }else{ 135 snakeObj.doMove(snakeObj.dir); 136 } 137 }else{ 138 // over 139 started = false; 140 oppDiv.style.display = "block"; 141 oppDiv.innerHTML = "点击再次游戏"; 142 return; 143 } 144 145 caCt.save(); 146 caCt.fillStyle = "#C6EEC6"; 147 caCt.fillRect(0,0,caW,caH); 148 149 caCt.font = '40px Helvetica'; 150 caCt.textAlign = "center"; 151 caCt.textBaseline = "middle"; 152 caCt.fillStyle = '#8CCDD8'; 153 caCt.strokeStyle = '#8CCDD8'; 154 caCt.fillText("WangXinsheng", caW/2,caH/2); 155 caCt.strokeText("WangXinsheng", caW/2,caH/2); 156 157 caCt.restore(); 158 159 snakeObj.draw(caCt); 160 161 // draw food 162 drawFood(); 163 164 lastFpsUpdateTime = (+new Date); 165 } 166 167 if (now - lastFpsUpdateTimeFood > snakeObj.speed*5) { 168 if(!foodObj.live){ 169 gen("food"); 170 }else{ 171 lastFpsUpdateTimeFood = (+new Date); 172 } 173 } 174 } 175 window.requestNextAnimationFrame(animate); 176 } 177 function drawFood(){ 178 if(foodObj.live){ 179 caCt.save(); 180 caCt.fillStyle = foodObj.color; 181 caCt.fillRect( 182 (foodObj.x-1)*blockW, 183 (foodObj.y-1)*blockW, 184 blockW, 185 blockW 186 ); 187 caCt.restore(); 188 } 189 } 190 function eventBund(){ 191 if(!v){ 192 document.addEventListener("keyup", onKeyPress, false); 193 }else{ 194 caObj.addEventListener("touchstart", onTouchStart, false); 195 caObj.addEventListener("touchmove", onTouchMove, false); 196 caObj.addEventListener("touchend", onTouchEnd, false); 197 caObj.addEventListener("touchcancel", stopEvent, false); 198 caObj.addEventListener("gesturestart", stopEvent, false); 199 caObj.addEventListener("gesturechange", stopEvent, false); 200 caObj.addEventListener("gestureend", stopEvent, false); 201 } 202 window.addEventListener("resize", doSize, false); 203 } 204 function onKeyPress(e){ 205 //console.log(e); 206 var dir = ""; 207 switch(e.keyCode){ 208 case 38: 209 dir =snakeObj.dir=="S"?"":"N"; 210 break; 211 case 40: 212 dir =snakeObj.dir=="N"?"":"S"; 213 break; 214 case 37: 215 dir =snakeObj.dir=="E"?"":"W"; 216 break; 217 case 39: 218 dir =snakeObj.dir=="W"?"":"E"; 219 break; 220 default:; 221 } 222 if(dir!="") 223 snakeObj.dir = dir; 224 } 225 function onTouchEnd(e){ 226 e.preventDefault(); 227 if(mouseTObj.x!=null && mouseTObj.y!=null 228 && mouseTMObj.x!=null && mouseTMObj.y!=null){ 229 var diffX = mouseTMObj.x - mouseTObj.x, 230 diffY = mouseTMObj.y - mouseTObj.y, 231 dir = ""; 232 if(Math.abs(diffY)>Math.abs(diffX)){ 233 dir = diffY>0?"S":"N"; 234 }else if(Math.abs(diffY)<Math.abs(diffX)){ 235 dir = diffX>0?"E":"W"; 236 } 237 switch(dir){ 238 case "N": 239 snakeObj.dir =snakeObj.dir=="S"?"S":"N"; 240 break; 241 case "S": 242 snakeObj.dir =snakeObj.dir=="N"?"N":"S"; 243 break; 244 case "W": 245 snakeObj.dir =snakeObj.dir=="E"?"E":"W"; 246 break; 247 case "E": 248 snakeObj.dir =snakeObj.dir=="W"?"W":"E"; 249 break; 250 default:; 251 } 252 mouseTObj.x = null; 253 mouseTObj.y = null; 254 mouseTMObj.x = null; 255 mouseTMObj.y = null; 256 } 257 e.stopPropagation(); 258 return false; 259 } 260 function onTouchMove(e){ 261 e.preventDefault(); 262 var touch = e.touches[0]; 263 mouseTMObj.x = touch.pageX; 264 mouseTMObj.y = touch.pageY; 265 e.stopPropagation(); 266 return false; 267 } 268 function onTouchStart(e){ 269 e.preventDefault(); 270 var touch = e.touches[0]; 271 mouseTObj.x = touch.pageX; 272 mouseTObj.y = touch.pageY; 273 e.stopPropagation(); 274 return false; 275 } 276 function stopEvent(e) { e.preventDefault(); e.stopPropagation(); } 277 var v = navigator.userAgent.toLowerCase().indexOf("android") != -1 || navigator.userAgent.toLowerCase().indexOf("iphone") != -1 || navigator.userAgent.toLowerCase().indexOf("ipad") != -1, 278 caW = window.innerWidth, 279 caH = window.innerHeight, 280 caObj = document.getElementById("gameWorld"), 281 caCt = caObj.getContext("2d"), 282 oppDiv = document.getElementById("opp"), 283 lastFpsUpdateTime = new Date, 284 lastFpsUpdateTimeFood = new Date, 285 started = false, 286 blockW = 0, 287 blockMinNum = 20, 288 blockWNum = 0, 289 blockHNum = 0, 290 snakeObj = null, 291 snakeColor = "gray", 292 initSpeed = 150, 293 liStr = "<li data-x='{%x%}' data-y='{%y%}' data-s='{%s%}'></li>", //s:0-empty,1-snake,2-food 294 foodObj = {"x":0,"y":0,"live":false,"color":"red"}, 295 mouseTObj = {"x":null,"y":null}, 296 mouseTMObj = {"x":null,"y":null} 297 ; 298 299 this.init = function () { 300 //*********init params******* 301 doVarInit(); 302 //*********init size and vars******* 303 doSize(); 304 //*********product bell and rabit******* 305 gen("snake"); 306 gen("food"); 307 //gen("yyy"); 308 //*********Event*********** 309 eventBund(); 310 //*********Gen*********** 311 //*********animate*********** 312 animate(); 313 } 314 this.start = function(){ 315 oppDiv.style.display = "none"; 316 started = true; 317 } 318 } 319 320 /* 321 * object 322 */ 323 function snake(bgC,live,dir,blockW,speed,body) { 324 this.bgC = bgC; // 涂色 325 this.live=live; // 存活状态 true,false 326 this.dir=dir; // 移动方向 N,S,W,E 327 this.blockW=blockW; // 方块边大小 328 this.speed=speed; // 速度 1000 329 this.body = body; // 蛇身体 330 } 331 snake.prototype.draw = function (context) { 332 context.save(); 333 context.fillStyle = "gray"; 334 for(var i = 0;i<this.body.length;i++){ 335 context.fillRect( 336 (this.body[i].x-1)*this.blockW, 337 (this.body[i].y-1)*this.blockW, 338 this.blockW, 339 this.blockW 340 ); 341 } 342 context.restore(); 343 } 344 snake.prototype.canMove = function (dir,xMax,yMax) { 345 var result = false, 346 nx = this.body[0].x, 347 ny = this.body[0].y; 348 // 边框检测 349 switch(dir){ 350 case "N": 351 result = this.body[0].y==1?false:true; 352 ny--; 353 break; 354 case "S": 355 result = this.body[0].y==yMax?false:true; 356 ny++; 357 break; 358 case "W": 359 result = this.body[0].x==1?false:true; 360 nx--; 361 break; 362 case "E": 363 result = this.body[0].x==xMax?false:true; 364 nx++; 365 break; 366 default:; 367 } 368 // 自己检测 369 //console.log(document.querySelector("#dataDom [data-x='"+nx+"'][data-y='"+ny+"']").getAttribute("data-s")); 370 if(result) 371 result = 372 ("1"==document.querySelector("#dataDom [data-x='"+nx+"'][data-y='"+ny+"']").getAttribute("data-s")) 373 ?false 374 :true; 375 return result; 376 } 377 snake.prototype.canEat = function (dir,food) { 378 var sx=this.body[0].x, 379 sy=this.body[0].y; 380 switch(dir){ 381 case "N": 382 sy--; 383 break; 384 case "S": 385 sy++; 386 break; 387 case "W": 388 sx-1; 389 break; 390 case "E": 391 sx+1; 392 break; 393 default:; 394 } 395 return (food.x==sx && food.y==sy); 396 } 397 // push第一个,pop最后一个 398 snake.prototype.doMove = function (dir) { 399 this.doEat(dir,null); 400 this.setSnakeSts(this.body[this.body.length-1].x,this.body[this.body.length-1].y,0); 401 this.body.splice(this.body.length-1,1); 402 } 403 // push第一个 404 snake.prototype.doEat = function (dir,food) { 405 switch(dir){ 406 case "N": 407 this.body.splice(0,0,{"x":this.body[0].x,"y":this.body[0].y-1}); 408 break; 409 case "S": 410 this.body.splice(0,0,{"x":this.body[0].x,"y":this.body[0].y+1}); 411 break; 412 case "W": 413 this.body.splice(0,0,{"x":this.body[0].x-1,"y":this.body[0].y}); 414 break; 415 case "E": 416 this.body.splice(0,0,{"x":this.body[0].x+1,"y":this.body[0].y}); 417 break; 418 default:; 419 } 420 this.setSnakeSts(this.body[0].x,this.body[0].y,1); 421 if(food!=null) 422 food.live = false; 423 } 424 snake.prototype.setSnakeSts = function(x,y,s){ 425 document.querySelector("#dataDom [data-x='"+x+"'][data-y='"+y+"']").setAttribute("data-s",s); 426 } 427 428 var gameWorldObj = null; 429 window.onload = function () { 430 document.getElementsByTagName('title')[0].innerHTML = "[WXS]贪吃蛇"; 431 snakeGameGo(); 432 } 433 function snakeGameGo(){ 434 if(gameWorldObj == null) 435 gameWorldObj = new gameWorld(); 436 gameWorldObj.init(); 437 var opp = document.getElementById("opp"); 438 opp.addEventListener("click", doPlay, false); 439 } 440 function doPlay(){gameWorldObj.init();gameWorldObj.start();}
运行文件下载地址: