JS+HTML怎么实现经典吃豆人游戏
短信预约 -IT技能 免费直播动态提醒
这篇文章主要介绍“JS+HTML怎么实现经典吃豆人游戏”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“JS+HTML怎么实现经典吃豆人游戏”文章能帮助大家解决问题。
项目结构
因需要四个文件即可实现,index.html 游戏主页,index.js 主页控制,game.js 游戏控制,favicon.png 图标。代码简单,易懂。后续小伙伴们还可以自己加入音乐!可以部署到服务器上,也可用浏览器直接打开index.html玩耍!!!
HTML代码
<html><head><meta charset="utf8"><title>Pac-Man . 吃豆游戏</title><link rel="shortcut icon" href="favicon.png" rel="external nofollow" ><style>body{background-color: #292929}*{padding:0;margin:0;}.wrapper{width: 960px;margin:0 auto;line-height:36px;text-align:center;color:#999;}canvas{display:block;background: #000;}.mod-botton{height: 32px;padding: 15px 0;text-align: center;}</style></head><body><div class="wrapper"><canvas id="canvas" width="960" height="640">不支持画布</canvas><p>按[空格]暂停或继续</p></div><script class="lazy" data-src="game.js"></script><script class="lazy" data-src="index.js"></script></body></html>
JS代码
//主程序,业务逻辑(function(){var _DATA = [//地图数据[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1],[1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1],[1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1],[1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1],[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],[1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,0,1],[1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,0,1],[1,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1],[1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,0,1,1,1,2,2,1,1,1,0,1,1,0,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,0,1,2,2,2,2,2,2,1,0,1,1,0,1,1,1,1,1,1],[0,0,0,0,0,0,0,0,0,0,1,2,2,2,2,2,2,1,0,0,0,0,0,0,0,0,0,0],[1,1,1,1,1,1,0,1,1,0,1,2,2,2,2,2,2,1,0,1,1,0,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1],[1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1],[1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1],[1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1],[1,0,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1],[1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1],[1,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,1],[1,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,0,1,1,0,1,1,1],[1,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1],[1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1],[1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1],[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]],_GOODS = {//能量豆'1,3':1,'26,3':1,'1,23':1,'26,23':1},_COS = [1,0,-1,0],_SIN = [0,1,0,-1],_COLOR = ['#F00','#F93','#0CF','#F9C'],//红,橙,_LIFE = 3,_SCORE = 0;//得分 var game = new Game('canvas');//启动页(function(){var stage = game.createStage();//logostage.createItem({x:game.width/2,y:game.height*.45,width:100,height:100,frames:3,draw:function(context){var t = Math.abs(5-this.times%10);context.fillStyle = '#FFE600';context.beginPath();context.arc(this.x,this.y,this.width/2,t*.04*Math.PI,(2-t*.04)*Math.PI,false);context.lineTo(this.x,this.y);context.closePath();context.fill();context.fillStyle = '#000';context.beginPath();context.arc(this.x+5,this.y-27,7,0,2*Math.PI,false);context.closePath();context.fill();}});//游戏名stage.createItem({x:game.width/2,y:game.height*.6,draw:function(context){context.font = 'bold 42px Helvetica';context.textAlign = 'center';context.textBaseline = 'middle';context.fillStyle = '#FFF';context.fillText('Pac-Man',this.x,this.y);}});//版权信息stage.createItem({x:game.width-12,y:game.height-5,draw:function(context){context.font = '14px Helvetica';context.textAlign = 'right';context.textBaseline = 'bottom';context.fillStyle = '#AAA';context.fillText('© passer-by.com',this.x,this.y);}});//事件绑定stage.bind('keydown',function(e){switch(e.keyCode){case 13:case 32:game.nextStage();break;}});})();//游戏主程序(function(){var stage,map,beans,player,times;stage = game.createStage({update:function(){var stage = this;if(stage.status==1){//场景正常运行items.forEach(function(item){if(map&&!map.get(item.coord.x,item.coord.y)&&!map.get(player.coord.x,player.coord.y)){var dx = item.x-player.x;var dy = item.y-player.y;if(dx*dx+dy*dy<750&&item.status!=4){//物体检测if(item.status==3){item.status = 4;_SCORE += 10;}else{stage.status = 3;stage.timeout = 30;}}}});if(JSON.stringify(beans.data).indexOf(0)<0){//当没有物品的时候,进入结束画面game.nextStage();}}else if(stage.status==3){//场景临时状态if(!stage.timeout){_LIFE--;if(_LIFE){stage.resetItems();}else{game.nextStage();return false;}}}}});//绘制地图map = stage.createMap({x:60,y:10,data:_DATA,cache:true,draw:function(context){context.lineWidth = 2;for(var j=0; j<this.y_length; j++){for(var i=0; i<this.x_length; i++){var value = this.get(i,j);if(value){var code = [0,0,0,0];if(this.get(i+1,j)&&!(this.get(i+1,j-1)&&this.get(i+1,j+1)&&this.get(i,j-1)&&this.get(i,j+1))){code[0]=1;}if(this.get(i,j+1)&&!(this.get(i-1,j+1)&&this.get(i+1,j+1)&&this.get(i-1,j)&&this.get(i+1,j))){code[1]=1;}if(this.get(i-1,j)&&!(this.get(i-1,j-1)&&this.get(i-1,j+1)&&this.get(i,j-1)&&this.get(i,j+1))){code[2]=1;}if(this.get(i,j-1)&&!(this.get(i-1,j-1)&&this.get(i+1,j-1)&&this.get(i-1,j)&&this.get(i+1,j))){code[3]=1;}if(code.indexOf(1)>-1){context.strokeStyle=value==2?"#FFF":"#09C";var pos = this.coord2position(i,j);switch(code.join('')){case '1100':context.beginPath();context.arc(pos.x+this.size/2,pos.y+this.size/2,this.size/2,Math.PI,1.5*Math.PI,false);context.stroke();context.closePath();break;case '0110':context.beginPath();context.arc(pos.x-this.size/2,pos.y+this.size/2,this.size/2,1.5*Math.PI,2*Math.PI,false);context.stroke();context.closePath();break;case '0011':context.beginPath();context.arc(pos.x-this.size/2,pos.y-this.size/2,this.size/2,0,.5*Math.PI,false);context.stroke();context.closePath();break;case '1001':context.beginPath();context.arc(pos.x+this.size/2,pos.y-this.size/2,this.size/2,.5*Math.PI,1*Math.PI,false);context.stroke();context.closePath();break;default:var dist = this.size/2;code.forEach(function(v,index){if(v){context.beginPath();context.moveTo(pos.x,pos.y);context.lineTo(pos.x-_COS[index]*dist,pos.y-_SIN[index]*dist);context.stroke();context.closePath();}});}}}}}}});//物品地图beans = stage.createMap({x:60,y:10,data:_DATA,frames:8,draw:function(context){for(var j=0; j<this.y_length; j++){for(var i=0; i<this.x_length; i++){if(!this.get(i,j)){var pos = this.coord2position(i,j);context.fillStyle = "#F5F5DC";if(_GOODS[i+','+j]){context.beginPath();context.arc(pos.x,pos.y,3+this.times%2,0,2*Math.PI,true);context.fill();context.closePath();}else{context.fillRect(pos.x-2,pos.y-2,4,4);}}}}}});//得分stage.createItem({x:690,y:100,draw:function(context){context.font = 'bold 28px Helvetica';context.textAlign = 'left';context.textBaseline = 'bottom';context.fillStyle = '#C33';context.fillText('SCORE',this.x,this.y);context.font = '28px Helvetica';context.textAlign = 'left';context.textBaseline = 'top';context.fillStyle = '#FFF';context.fillText(_SCORE,this.x+12,this.y);}});//状态文字stage.createItem({x:690,y:320,frames:25,draw:function(context){if(stage.status==2&&this.times%2){context.font = '24px Helvetica';context.textAlign = 'left';context.textBaseline = 'center';context.fillStyle = '#09F';context.fillText('PAUSE',this.x,this.y);}}});//生命值stage.createItem({x:705,y:540,width:30,height:30,draw:function(context){for(var i=0;i<_LIFE-1;i++){var x=this.x+40*i,y=this.y;context.fillStyle = '#FFE600';context.beginPath();context.arc(x,y,this.width/2,.15*Math.PI,-.15*Math.PI,false);context.lineTo(x,y);context.closePath();context.fill();}}});//NPCfor(var i=0;i<4;i++){stage.createItem({width:30,height:30,orientation:3,color:_COLOR[i],location:map,coord:{x:12+i,y:14},vector:{x:12+i,y:14},type:2,frames:10,speed:1,timeout:Math.floor(Math.random()*120),update:function(){var new_map;if(this.status==3&&!this.timeout){this.status = 1;}if(!this.coord.offset){//到达坐标中心时计算if(this.status==1){if(!this.timeout){//定时器new_map = JSON.parse(JSON.stringify(map.data).replace(/2/g,0));var id = this._id;items.forEach(function(item){if(item._id!=id&&item.status==1){//NPC将其它所有还处于正常状态的NPC当成一堵墙new_map[item.coord.y][item.coord.x]=1;}});this.path = map.finder({map:new_map,start:this.coord,end:player.coord});if(this.path.length){this.vector = this.path[0];}}}else if(this.status==3){new_map = JSON.parse(JSON.stringify(map.data).replace(/2/g,0));var id = this._id;items.forEach(function(item){if(item._id!=id){new_map[item.coord.y][item.coord.x]=1;}});this.path = map.finder({map:new_map,start:player.coord,end:this.coord,type:'next'});if(this.path.length){this.vector = this.path[Math.floor(Math.random()*this.path.length)];}}else if(this.status==4){new_map = JSON.parse(JSON.stringify(map.data).replace(/2/g,0));this.path = map.finder({map:new_map,start:this.coord,end:this._params.coord});if(this.path.length){this.vector = this.path[0];}else{this.status = 1;}}//是否转变方向if(this.vector.change){this.coord.x = this.vector.x;this.coord.y = this.vector.y;var pos = map.coord2position(this.coord.x,this.coord.y);this.x = pos.x;this.y = pos.y;}//方向判定if(this.vector.x>this.coord.x){this.orientation = 0;}else if(this.vector.x<this.coord.x){this.orientation = 2;}else if(this.vector.y>this.coord.y){this.orientation = 1;}else if(this.vector.y<this.coord.y){this.orientation = 3;}}this.x += this.speed*_COS[this.orientation];this.y += this.speed*_SIN[this.orientation];},draw:function(context){var isSick = false;if(this.status==3){isSick = this.timeout>80||this.times%2?true:false;}if(this.status!=4){context.fillStyle = isSick?'#BABABA':this.color;context.beginPath();context.arc(this.x,this.y,this.width*.5,0,Math.PI,true);switch(this.times%2){case 0:context.lineTo(this.x-this.width*.5,this.y+this.height*.4);context.quadraticCurveTo(this.x-this.width*.4,this.y+this.height*.5,this.x-this.width*.2,this.y+this.height*.3);context.quadraticCurveTo(this.x,this.y+this.height*.5,this.x+this.width*.2,this.y+this.height*.3);context.quadraticCurveTo(this.x+this.width*.4,this.y+this.height*.5,this.x+this.width*.5,this.y+this.height*.4);break;case 1:context.lineTo(this.x-this.width*.5,this.y+this.height*.3);context.quadraticCurveTo(this.x-this.width*.25,this.y+this.height*.5,this.x,this.y+this.height*.3);context.quadraticCurveTo(this.x+this.width*.25,this.y+this.height*.5,this.x+this.width*.5,this.y+this.height*.3);break;}context.fill();context.closePath();}context.fillStyle = '#FFF';if(isSick){context.beginPath();context.arc(this.x-this.width*.15,this.y-this.height*.21,this.width*.08,0,2*Math.PI,false);context.arc(this.x+this.width*.15,this.y-this.height*.21,this.width*.08,0,2*Math.PI,false);context.fill();context.closePath();}else{context.beginPath();context.arc(this.x-this.width*.15,this.y-this.height*.21,this.width*.12,0,2*Math.PI,false);context.arc(this.x+this.width*.15,this.y-this.height*.21,this.width*.12,0,2*Math.PI,false);context.fill();context.closePath();context.fillStyle = '#000';context.beginPath();context.arc(this.x-this.width*(.15-.04*_COS[this.orientation]),this.y-this.height*(.21-.04*_SIN[this.orientation]),this.width*.07,0,2*Math.PI,false);context.arc(this.x+this.width*(.15+.04*_COS[this.orientation]),this.y-this.height*(.21-.04*_SIN[this.orientation]),this.width*.07,0,2*Math.PI,false);context.fill();context.closePath();}}});}items = stage.getItemsByType(2);//主角player = stage.createItem({width:30,height:30,type:1,location:map,coord:{x:13.5,y:23},orientation:2,speed:2,frames:10,update:function(){var coord = this.coord;if(!coord.offset){if(this.control.orientation!='undefined'){if(!map.get(coord.x+_COS[this.control.orientation],coord.y+_SIN[this.control.orientation])){this.orientation = this.control.orientation;}}this.control = {};var value = map.get(coord.x+_COS[this.orientation],coord.y+_SIN[this.orientation]);if(value==0){this.x += this.speed*_COS[this.orientation];this.y += this.speed*_SIN[this.orientation];}else if(value<0){this.x -= map.size*(map.x_length-1)*_COS[this.orientation];this.y -= map.size*(map.y_length-1)*_SIN[this.orientation];}}else{if(!beans.get(this.coord.x,this.coord.y)){//吃豆_SCORE++;beans.set(this.coord.x,this.coord.y,1);if(_GOODS[this.coord.x+','+this.coord.y]){//吃到能量豆items.forEach(function(item){if(item.status==1||item.status==3){//如果NPC为正常状态,则置为临时状态item.timeout = 450;item.status = 3;}});}}this.x += this.speed*_COS[this.orientation];this.y += this.speed*_SIN[this.orientation];}},draw:function(context){context.fillStyle = '#FFE600';context.beginPath();if(stage.status!=3){//玩家正常状态if(this.times%2){context.arc(this.x,this.y,this.width/2,(.5*this.orientation+.20)*Math.PI,(.5*this.orientation-.20)*Math.PI,false);}else{context.arc(this.x,this.y,this.width/2,(.5*this.orientation+.01)*Math.PI,(.5*this.orientation-.01)*Math.PI,false);}}else{//玩家被吃if(stage.timeout) {context.arc(this.x,this.y,this.width/2,(.5*this.orientation+1-.02*stage.timeout)*Math.PI,(.5*this.orientation-1+.02*stage.timeout)*Math.PI,false);}}context.lineTo(this.x,this.y);context.closePath();context.fill();}});//事件绑定stage.bind('keydown',function(e){switch(e.keyCode){case 13: //回车case 32: //空格this.status = this.status==2?1:2;break;case 39: //右player.control = {orientation:0};break;case 40: //下player.control = {orientation:1};break;case 37: //左player.control = {orientation:2};break;case 38: //上player.control = {orientation:3};break;}});})();//结束画面(function(){var stage = game.createStage();//游戏结束stage.createItem({x:game.width/2,y:game.height*.35,draw:function(context){context.fillStyle = '#FFF';context.font = 'bold 48px Helvetica';context.textAlign = 'center';context.textBaseline = 'middle';context.fillText('GAME OVER',this.x,this.y);}});//记分stage.createItem({x:game.width/2,y:game.height*.5,draw:function(context){context.fillStyle = '#FFF';context.font = '20px Helvetica';context.textAlign = 'center';context.textBaseline = 'middle';context.fillText('FINAL SCORE: '+(_SCORE+50*Math.max(_LIFE-1,0)),this.x,this.y);}});//事件绑定stage.bind('keydown',function(e){switch(e.keyCode){case 13: //回车case 32: //空格_SCORE = 0;_LIFE = 3;var st = game.setStage(1);st.reset();break;}});})();game.init();})();
游戏截图
操作说明 ,按键盘上下左右键移动 ,按键盘空格键暂停!
关于“JS+HTML怎么实现经典吃豆人游戏”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341